This is an automated email from the ASF dual-hosted git repository.
ddanielr pushed a commit to branch 2.1
in repository https://gitbox.apache.org/repos/asf/accumulo.git
The following commit(s) were added to refs/heads/2.1 by this push:
new 8779467e73 Allows the monitor to be hosted behind a proxy (#5729)
8779467e73 is described below
commit 8779467e73cc3b09b512499ee669a31353ea7454
Author: Daniel Roberts <[email protected]>
AuthorDate: Thu Jul 17 11:28:03 2025 -0400
Allows the monitor to be hosted behind a proxy (#5729)
Changes the templates to use a base href path vs hardcoded absolute
paths and changes links in js files to use the base href value.
Allows the user to set a system property to define the root context of
the monitor.
Update the js files and functions created by FreeMarker to use the
contextPath when making requests.
Adds test for EmbeddedWebServer to check if the contextPath is correct.
Also adds preconditions to Monitor.java to check the format of the root
context value.
---
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 | 4 +-
.../accumulo/monitor/resources/js/compactions.js | 4 +-
.../org/apache/accumulo/monitor/resources/js/ec.js | 8 +-
.../accumulo/monitor/resources/js/functions.js | 60 +++++++-------
.../org/apache/accumulo/monitor/resources/js/gc.js | 2 +-
.../apache/accumulo/monitor/resources/js/global.js | 2 +
.../accumulo/monitor/resources/js/manager.js | 6 +-
.../accumulo/monitor/resources/js/problems.js | 8 +-
.../accumulo/monitor/resources/js/replication.js | 2 +-
.../apache/accumulo/monitor/resources/js/scans.js | 4 +-
.../apache/accumulo/monitor/resources/js/server.js | 4 +-
.../apache/accumulo/monitor/resources/js/table.js | 4 +-
.../accumulo/monitor/resources/js/tservers.js | 8 +-
.../apache/accumulo/monitor/templates/default.ftl | 49 ++++++------
.../org/apache/accumulo/monitor/templates/log.ftl | 2 +-
.../apache/accumulo/monitor/templates/modals.ftl | 2 +-
.../apache/accumulo/monitor/templates/navbar.ftl | 30 +++----
.../apache/accumulo/monitor/templates/overview.ftl | 8 +-
.../apache/accumulo/monitor/templates/tables.ftl | 4 +-
.../accumulo/monitor/EmbeddedWebServerTest.java | 93 ++++++++++++++++++++++
27 files changed, 256 insertions(+), 107 deletions(-)
diff --git a/assemble/pom.xml b/assemble/pom.xml
index 2d933e582f..1236bbb9c1 100644
--- a/assemble/pom.xml
+++ b/assemble/pom.xml
@@ -126,6 +126,11 @@
<artifactId>commons-logging</artifactId>
<optional>true</optional>
</dependency>
+ <dependency>
+ <groupId>commons-validator</groupId>
+ <artifactId>commons-validator</artifactId>
+ <optional>true</optional>
+ </dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-commons</artifactId>
diff --git a/core/src/main/java/org/apache/accumulo/core/conf/Property.java
b/core/src/main/java/org/apache/accumulo/core/conf/Property.java
index d9d94cb367..625cb0ed1f 100644
--- a/core/src/main/java/org/apache/accumulo/core/conf/Property.java
+++ b/core/src/main/java/org/apache/accumulo/core/conf/Property.java
@@ -1037,6 +1037,11 @@ 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_ROOT_CONTEXT("monitor.root.context", "/", PropertyType.STRING,
+ "The root context path of the monitor 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"),
diff --git a/pom.xml b/pom.xml
index c8aa687bbb..fdcab6190e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -322,6 +322,11 @@
<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>
diff --git a/server/monitor/pom.xml b/server/monitor/pom.xml
index cb2e899597..23ecd0d691 100644
--- a/server/monitor/pom.xml
+++ b/server/monitor/pom.xml
@@ -52,6 +52,10 @@
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
+ <dependency>
+ <groupId>commons-validator</groupId>
+ <artifactId>commons-validator</artifactId>
+ </dependency>
<dependency>
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
diff --git
a/server/monitor/src/main/java/org/apache/accumulo/monitor/EmbeddedWebServer.java
b/server/monitor/src/main/java/org/apache/accumulo/monitor/EmbeddedWebServer.java
index 696cbe6a96..8249d231c0 100644
---
a/server/monitor/src/main/java/org/apache/accumulo/monitor/EmbeddedWebServer.java
+++
b/server/monitor/src/main/java/org/apache/accumulo/monitor/EmbeddedWebServer.java
@@ -22,6 +22,7 @@ import java.util.EnumSet;
import org.apache.accumulo.core.conf.AccumuloConfiguration;
import org.apache.accumulo.core.conf.Property;
+import org.apache.commons.validator.routines.UrlValidator;
import org.eclipse.jetty.server.AbstractConnectionFactory;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
@@ -33,6 +34,9 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+
public class EmbeddedWebServer {
private static final Logger LOG =
LoggerFactory.getLogger(EmbeddedWebServer.class);
@@ -57,7 +61,17 @@ public class EmbeddedWebServer {
handler =
new ServletContextHandler(ServletContextHandler.SESSIONS |
ServletContextHandler.SECURITY);
handler.getSessionHandler().getSessionCookieConfig().setHttpOnly(true);
- handler.setContextPath("/");
+ String rootContext =
monitor.getConfiguration().get(Property.MONITOR_ROOT_CONTEXT);
+ // Validate URL
+ String[] scheme = {"https"};
+ UrlValidator validator = new UrlValidator(scheme);
+ Preconditions.checkArgument(validator.isValid("https://example.com" +
rootContext),
+ "Root context: \"%s\" is not a valid URL", rootContext);
+ // Remove the trailing slash since jetty will warn otherwise.
+ if (rootContext.length() > 1 && rootContext.endsWith("/")) {
+ rootContext = rootContext.substring(0, rootContext.length() - 1);
+ }
+ handler.setContextPath(rootContext);
}
private static AbstractConnectionFactory[]
getConnectionFactories(AccumuloConfiguration conf,
@@ -108,6 +122,11 @@ public class EmbeddedWebServer {
handler.addServlet(restServlet, where);
}
+ @VisibleForTesting
+ String getContextPath() {
+ return handler.getContextPath();
+ }
+
public String getHostName() {
return connector.getHost();
}
diff --git
a/server/monitor/src/main/java/org/apache/accumulo/monitor/Monitor.java
b/server/monitor/src/main/java/org/apache/accumulo/monitor/Monitor.java
index f798ad3c30..f9672ed196 100644
--- a/server/monitor/src/main/java/org/apache/accumulo/monitor/Monitor.java
+++ b/server/monitor/src/main/java/org/apache/accumulo/monitor/Monitor.java
@@ -105,6 +105,7 @@ import org.glassfish.jersey.servlet.ServletContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Preconditions;
import com.google.common.base.Suppliers;
/**
@@ -453,6 +454,10 @@ public class Monitor extends AbstractServer implements
HighlyAvailableService {
public void run() {
ServerContext context = getContext();
int[] ports = getConfiguration().getPort(Property.MONITOR_PORT);
+ String rootContext = getConfiguration().get(Property.MONITOR_ROOT_CONTEXT);
+ // Needs leading slash in order to property create rest endpoint requests
+ Preconditions.checkArgument(rootContext.startsWith("/"),
+ "Root context: \"%s\" does not have a leading '/'", rootContext);
for (int port : ports) {
try {
log.debug("Trying monitor on port {}", port);
@@ -508,9 +513,13 @@ public class Monitor extends AbstractServer implements
HighlyAvailableService {
metricsInfo.init(MetricsInfo.serviceTags(getContext().getInstanceName(),
getApplicationName(),
monitorHostAndPort, ""));
+ // 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();
// Delete before we try to re-create in case the previous session hasn't
yet expired
diff --git
a/server/monitor/src/main/java/org/apache/accumulo/monitor/view/WebViews.java
b/server/monitor/src/main/java/org/apache/accumulo/monitor/view/WebViews.java
index cb69bf9e73..92b9701709 100644
---
a/server/monitor/src/main/java/org/apache/accumulo/monitor/view/WebViews.java
+++
b/server/monitor/src/main/java/org/apache/accumulo/monitor/view/WebViews.java
@@ -93,12 +93,18 @@ public class WebViews {
}
private Map<String,Object> getModel() {
-
+ AccumuloConfiguration conf = monitor.getContext().getConfiguration();
+ String rootContext = conf.get(Property.MONITOR_ROOT_CONTEXT);
+ // Add trailing slash if it doesn't exist
+ if (!rootContext.endsWith("/")) {
+ rootContext = rootContext + "/";
+ }
Map<String,Object> model = new HashMap<>();
model.put("version", Constants.VERSION);
model.put("instance_name", monitor.getContext().getInstanceName());
model.put("instance_id", monitor.getContext().getInstanceID());
model.put("zk_hosts", monitor.getContext().getZooKeepers());
+ model.put("rootContext", rootContext);
addExternalResources(model);
return model;
}
diff --git
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
index dbc9350c35..a89bc95ad4 100644
---
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
@@ -40,7 +40,7 @@ function refresh() {
*/
$(document).ready(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
@@ -85,7 +85,7 @@ $(document).ready(function () {
"type": "html",
"render": function (data, type) {
if (type === 'display') {
- data = `<a href="/tservers?s=${data}">${data}</a>`;
+ data = `<a href="tservers?s=${data}">${data}</a>`;
}
return data;
}
diff --git
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/compactions.js
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/compactions.js
index 1275518c20..e0737de9e5 100644
---
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/compactions.js
+++
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/compactions.js
@@ -26,7 +26,7 @@ $(document).ready(function () {
// Create a table for compactions list
compactionsList = $('#compactionsList').DataTable({
"ajax": {
- "url": '/rest/compactions',
+ "url": contextPath + 'rest/compactions',
"dataSrc": "compactions"
},
"stateSave": true,
@@ -51,7 +51,7 @@ $(document).ready(function () {
"type": "html",
"render": function (data, type, row, meta) {
if (type === 'display') {
- data = '<a href="/tservers?s=' + row.server + '">' + row.server +
'</a>';
+ data = '<a href="tservers?s=' + row.server + '">' + row.server +
'</a>';
}
return data;
}
diff --git
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/ec.js
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/ec.js
index 545c2559df..9ec7c98339 100644
---
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/ec.js
+++
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/ec.js
@@ -37,7 +37,7 @@ $(document).ready(function () {
compactorsTable = $('#compactorsTable').DataTable({
"ajax": {
- "url": '/rest/ec/compactors',
+ "url": contextPath + 'rest/ec/compactors',
"dataSrc": "compactors"
},
"stateSave": true,
@@ -72,7 +72,7 @@ $(document).ready(function () {
// Create a table for running compactors
runningTable = $('#runningTable').DataTable({
"ajax": {
- "url": '/rest/ec/running',
+ "url": contextPath + 'rest/ec/running',
"dataSrc": "running"
},
"stateSave": true,
@@ -145,7 +145,7 @@ $(document).ready(function () {
// Create a table for compaction coordinator
coordinatorTable = $('#coordinatorTable').DataTable({
"ajax": {
- "url": '/rest/ec',
+ "url": contextPath + 'rest/ec',
"dataSrc": function (data) {
// the data needs to be in an array to work with DataTables
var arr = [];
@@ -275,7 +275,7 @@ async function refreshCoordinatorStatus() {
}
function getRunningDetails(ecid, idSuffix) {
- var ajaxUrl = '/rest/ec/details?ecid=' + ecid;
+ var ajaxUrl = contextPath + 'rest/ec/details?ecid=' + ecid;
console.log("Ajax call to " + ajaxUrl);
$.getJSON(ajaxUrl, function (data) {
populateDetails(data, idSuffix);
diff --git
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
index 36a46f69e8..30705be653 100644
---
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
@@ -340,21 +340,21 @@ function doLoggedPostCall(call, callback, shouldSanitize)
{
* stores it on a sessionStorage variable
*/
function getManager() {
- return getJSONForTable('/rest/manager', 'manager');
+ return getJSONForTable(contextPath + 'rest/manager', 'manager');
}
/**
* REST GET call for the namespaces, stores it on a global variable
*/
function getNamespaces() {
- return getJSONForTable('/rest/tables/namespaces', 'NAMESPACES');
+ return getJSONForTable(contextPath + 'rest/tables/namespaces', 'NAMESPACES');
}
/**
* REST GET call for the tables, stores it on a sessionStorage variable
*/
function getTables() {
- return getJSONForTable('/rest/tables', 'tables');
+ return getJSONForTable(contextPath + 'rest/tables', 'tables');
}
/**
@@ -378,7 +378,7 @@ function getNamespaceTables(namespaces) {
// Convert the list to a string for the REST call
namespaceList = namespaces.toString();
- return getJSONForTable('/rest/tables/namespaces/' + namespaceList, 'tables');
+ return getJSONForTable(contextPath + 'rest/tables/namespaces/' +
namespaceList, 'tables');
}
/**
@@ -387,14 +387,14 @@ function getNamespaceTables(namespaces) {
* @param {string} server Dead Server ID
*/
function clearDeadServers(server) {
- doLoggedPostCall('/rest/tservers?server=' + server, null, false);
+ doLoggedPostCall(contextPath + 'rest/tservers?server=' + server, null,
false);
}
/**
* REST GET call for the tservers, stores it on a sessionStorage variable
*/
function getTServers() {
- return getJSONForTable('/rest/tservers', 'tservers');
+ return getJSONForTable(contextPath + 'rest/tservers', 'tservers');
}
/**
@@ -403,35 +403,35 @@ function getTServers() {
* @param {string} server Server ID
*/
function getTServer(server) {
- return getJSONForTable('/rest/tservers/' + server, 'server');
+ return getJSONForTable(contextPath + 'rest/tservers/' + server, 'server');
}
/**
* REST GET call for the scans, stores it on a sessionStorage variable
*/
function getScans() {
- return getJSONForTable('/rest/scans', 'scans');
+ return getJSONForTable(contextPath + 'rest/scans', 'scans');
}
/**
* REST GET call for the bulk imports, stores it on a sessionStorage variable
*/
function getBulkImports() {
- return getJSONForTable('/rest/bulkImports', 'bulkImports');
+ return getJSONForTable(contextPath + 'rest/bulkImports', 'bulkImports');
}
/**
* REST GET call for the server stats, stores it on a sessionStorage variable
*/
function getServerStats() {
- return getJSONForTable('/rest/tservers/serverStats', 'serverStats');
+ return getJSONForTable(contextPath + 'rest/tservers/serverStats',
'serverStats');
}
/**
* REST GET call for the recovery list, stores it on a sessionStorage variable
*/
function getRecoveryList() {
- return getJSONForTable('/rest/tservers/recovery', 'recoveryList');
+ return getJSONForTable(contextPath + 'rest/tservers/recovery',
'recoveryList');
}
/**
@@ -441,21 +441,21 @@ function getRecoveryList() {
* @param {string} table Table ID
*/
function getTableServers(tableID) {
- return getJSONForTable('/rest/tables/' + tableID, 'tableServers');
+ return getJSONForTable(contextPath + 'rest/tables/' + tableID,
'tableServers');
}
/**
* REST GET call for the logs, stores it on a sessionStorage variable
*/
function getLogs() {
- return getJSONForTable('/rest/logs', 'logs');
+ return getJSONForTable(contextPath + 'rest/logs', 'logs');
}
/**
* REST POST call to clear logs
*/
function clearLogs() {
- doLoggedPostCall('/rest/logs/clear', refresh, false);
+ doLoggedPostCall(contextPath + 'rest/logs/clear', refresh, false);
}
/**
@@ -464,7 +464,7 @@ function clearLogs() {
* @param {string} tableID Table ID
*/
function clearTableProblems(tableID) {
- doLoggedPostCall('/rest/problems/summary?s=' + tableID, refresh, true);
+ doLoggedPostCall(contextPath + 'rest/problems/summary?s=' + tableID,
refresh, true);
}
/**
@@ -475,7 +475,7 @@ function clearTableProblems(tableID) {
* @param {string} type Type of problem
*/
function clearDetailsProblems(table, resource, type) {
- doLoggedPostCall('/rest/problems/details?table=' + table + '&resource=' +
+ doLoggedPostCall(contextPath + 'rest/problems/details?table=' + table +
'&resource=' +
resource + '&ptype=' + type, refresh, true);
}
@@ -484,7 +484,7 @@ function clearDetailsProblems(table, resource, type) {
* stores it on a sessionStorage variable
*/
function getProblemSummary() {
- return getJSONForTable('/rest/problems/summary', 'problemSummary');
+ return getJSONForTable(contextPath + 'rest/problems/summary',
'problemSummary');
}
/**
@@ -492,7 +492,7 @@ function getProblemSummary() {
* stores it on a sessionStorage variable
*/
function getProblemDetails() {
- return getJSONForTable('/rest/problems/details', 'problemDetails');
+ return getJSONForTable(contextPath + 'rest/problems/details',
'problemDetails');
}
/**
@@ -500,7 +500,7 @@ function getProblemDetails() {
* stores it on a sessionStorage variable
*/
function getReplication() {
- return getJSONForTable('/rest/replication', 'replication');
+ return getJSONForTable(contextPath + 'rest/replication', 'replication');
}
//// Overview Plots Rest Calls
@@ -510,7 +510,7 @@ function getReplication() {
* stores it on a sessionStorage variable
*/
function getIngestRate() {
- return getJSONForTable('/rest/statistics/time/ingestRate', 'ingestRate');
+ return getJSONForTable(contextPath + 'rest/statistics/time/ingestRate',
'ingestRate');
}
/**
@@ -518,7 +518,7 @@ function getIngestRate() {
* stores it on a sessionStorage variable
*/
function getScanEntries() {
- return getJSONForTable('/rest/statistics/time/scanEntries', 'scanEntries');
+ return getJSONForTable(contextPath + 'rest/statistics/time/scanEntries',
'scanEntries');
}
/**
@@ -526,28 +526,28 @@ function getScanEntries() {
* stores it on a sessionStorage variable
*/
function getIngestByteRate() {
- return getJSONForTable('/rest/statistics/time/ingestByteRate', 'ingestMB');
+ return getJSONForTable(contextPath + 'rest/statistics/time/ingestByteRate',
'ingestMB');
}
/**
* REST GET call for the query byte rate, stores it on a sessionStorage
variable
*/
function getQueryByteRate() {
- return getJSONForTable('/rest/statistics/time/queryByteRate', 'queryMB');
+ return getJSONForTable(contextPath + 'rest/statistics/time/queryByteRate',
'queryMB');
}
/**
* REST GET call for the load average, stores it on a sessionStorage variable
*/
function getLoadAverage() {
- return getJSONForTable('/rest/statistics/time/load', 'loadAvg');
+ return getJSONForTable(contextPath + 'rest/statistics/time/load', 'loadAvg');
}
/**
* REST GET call for the lookups, stores it on a sessionStorage variable
*/
function getLookups() {
- return getJSONForTable('/rest/statistics/time/lookups', 'lookups');
+ return getJSONForTable(contextPath + 'rest/statistics/time/lookups',
'lookups');
}
/**
@@ -555,7 +555,7 @@ function getLookups() {
* stores it on a sessionStorage variable
*/
function getMinorCompactions() {
- return getJSONForTable('/rest/statistics/time/minorCompactions',
'minorCompactions');
+ return getJSONForTable(contextPath +
'rest/statistics/time/minorCompactions', 'minorCompactions');
}
/**
@@ -563,7 +563,7 @@ function getMinorCompactions() {
* stores it on a sessionStorage variable
*/
function getMajorCompactions() {
- return getJSONForTable('/rest/statistics/time/majorCompactions',
'majorCompactions');
+ return getJSONForTable(contextPath +
'rest/statistics/time/majorCompactions', 'majorCompactions');
}
/**
@@ -571,7 +571,7 @@ function getMajorCompactions() {
* stores it on a sessionStorage variable
*/
function getIndexCacheHitRate() {
- return getJSONForTable('/rest/statistics/time/indexCacheHitRate',
'indexCache');
+ return getJSONForTable(contextPath +
'rest/statistics/time/indexCacheHitRate', 'indexCache');
}
/**
@@ -579,14 +579,14 @@ function getIndexCacheHitRate() {
* stores it on a sessionStorage variable
*/
function getDataCacheHitRate() {
- return getJSONForTable('/rest/statistics/time/dataCacheHitRate',
'dataCache');
+ return getJSONForTable(contextPath +
'rest/statistics/time/dataCacheHitRate', 'dataCache');
}
/**
* REST GET call for the server status, stores it on a sessionStorage variable
*/
function getStatus() {
- return getJSONForTable('/rest/status', 'status');
+ return getJSONForTable(contextPath + 'rest/status', 'status');
}
/*
diff --git
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/gc.js
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/gc.js
index 6b1a8d4119..63e9a75307 100644
---
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/gc.js
+++
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/gc.js
@@ -26,7 +26,7 @@ $(document).ready(function () {
// Create a table for compactions list
gcTable = $('#gcActivity').DataTable({
"ajax": {
- "url": '/rest/gc',
+ "url": contextPath + 'rest/gc',
"dataSrc": "stats"
},
"stateSave": true,
diff --git
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/global.js
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/global.js
index d4562b7e03..7b6c33526b 100644
---
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/global.js
+++
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/global.js
@@ -28,5 +28,7 @@ var NAMESPACES = '';
*/
var TIMER;
+const contextPath = $("base").attr("href");
+
const EMPTY_CELL = "<td>-</td>";
const EMPTY_ROW_THREE_CELLS = "<tr>" + EMPTY_CELL + EMPTY_CELL + EMPTY_CELL +
"</tr>";
diff --git
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/manager.js
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/manager.js
index 716140c4f1..cdc5942197 100644
---
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/manager.js
+++
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/manager.js
@@ -95,7 +95,7 @@ $(document).ready(function () {
// Generates the manager table
managerStatusTable = $('#managerStatus').DataTable({
"ajax": {
- "url": '/rest/manager',
+ "url": contextPath + 'rest/manager',
"dataSrc": function (json) {
// the data needs to be in an array to work with DataTables
var arr = [json];
@@ -151,7 +151,7 @@ $(document).ready(function () {
if (data !== 'Waiting') {
data = dateFormat(parseInt(data, 10));
}
- data = '<a href="/gc">' + data + '</a>';
+ data = '<a href="gc">' + data + '</a>';
}
return data;
}
@@ -186,7 +186,7 @@ $(document).ready(function () {
// Generates the recovery table
recoveryListTable = $('#recoveryList').DataTable({
"ajax": {
- "url": '/rest/tservers/recovery',
+ "url": contextPath + 'rest/tservers/recovery',
"dataSrc": function (data) {
data = data.recoveryList;
if (data.length === 0) {
diff --git
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/problems.js
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/problems.js
index 051a40d06e..9510d073be 100644
---
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/problems.js
+++
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/problems.js
@@ -25,7 +25,7 @@ $(document).ready(function () {
// Create a table for summary. See datatables doc for more info on the dom
property
problemSummaryTable = $('#problemSummary').DataTable({
"ajax": {
- "url": '/rest/problems/summary',
+ "url": contextPath + 'rest/problems/summary',
"dataSrc": "problemSummary"
},
"stateSave": true,
@@ -41,7 +41,7 @@ $(document).ready(function () {
"data": "tableName",
"type": "html",
"render": function (data, type, row, meta) {
- if (type === 'display') data = '<a href="/tables/' + row.tableID +
'">' + row.tableName + '</a>';
+ if (type === 'display') data = '<a href="tables/' + row.tableID +
'">' + row.tableName + '</a>';
return data;
}
},
@@ -68,7 +68,7 @@ $(document).ready(function () {
// Create a table for details
problemDetailTable = $('#problemDetails').DataTable({
"ajax": {
- "url": '/rest/problems/details',
+ "url": contextPath + 'rest/problems/details',
"dataSrc": "problemDetails"
},
"stateSave": true,
@@ -84,7 +84,7 @@ $(document).ready(function () {
"data": "tableName",
"type": "html",
"render": function (data, type, row, meta) {
- if (type === 'display') data = '<a href="/tables/' + row.tableID +
'">' + row.tableName + '</a>';
+ if (type === 'display') data = '<a href="tables/' + row.tableID +
'">' + row.tableName + '</a>';
return data;
}
},
diff --git
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/replication.js
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/replication.js
index bdfd2fe9b2..175975646b 100644
---
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/replication.js
+++
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/replication.js
@@ -41,7 +41,7 @@ $(document).ready(function () {
replicationStatsTable = $('#replicationStats').DataTable({
"ajax": {
- "url": "/rest/replication",
+ "url": contextPath + "rest/replication",
"dataSrc": ""
},
"stateSave": true,
diff --git
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/scans.js
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/scans.js
index d9b1db7934..d2ba29881c 100644
---
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/scans.js
+++
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/scans.js
@@ -27,7 +27,7 @@ $(document).ready(function () {
// Create a table for scans list
scansList = $('#scansList').DataTable({
"ajax": {
- "url": '/rest/scans',
+ "url": contextPath + 'rest/scans',
"dataSrc": "scans"
},
"stateSave": true,
@@ -52,7 +52,7 @@ $(document).ready(function () {
"type": "html",
"render": function (data, type, row, meta) {
if (type === 'display') {
- data = '<a href="/tservers?s=' + row.server + '">' + row.server +
'</a>';
+ data = '<a href="tservers?s=' + row.server + '">' + row.server +
'</a>';
}
return data;
}
diff --git
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/server.js
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/server.js
index 5119bd50f7..91560cc02c 100644
---
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/server.js
+++
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/server.js
@@ -44,7 +44,7 @@ function refresh() {
*/
function initServerTables(serv) {
- const url = '/rest/tservers/' + serv;
+ const url = contextPath + 'rest/tservers/' + serv;
console.debug('REST url used to fetch data for server.js DataTables: ' +
url);
// Create a table for details on the current server
@@ -253,7 +253,7 @@ function initServerTables(serv) {
"type": "html",
"render": function (data, type, row) {
if (type === 'display') {
- data = `<a href="/tables/${row.tableID}">${data}</a>`;
+ data = `<a href="tables/${row.tableID}">${data}</a>`;
}
return data;
}
diff --git
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
index 569138a53c..9a37094149 100644
---
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
@@ -45,7 +45,7 @@ function getQueuedAndRunning(data) {
*/
function initTableServerTable(tableID) {
- const url = '/rest/tables/' + tableID;
+ const url = contextPath + 'rest/tables/' + tableID;
console.debug('REST url used to fetch data for table.js DataTable: ' + url);
tableServersTable = $('#participatingTServers').DataTable({
@@ -109,7 +109,7 @@ function initTableServerTable(tableID) {
"type": "html",
"render": function (data, type, row) {
if (type === 'display') {
- data = `<a href="/tservers?s=${row.id}">${data}</a>`;
+ data = `<a href="tservers?s=${row.id}">${data}</a>`;
}
return data;
}
diff --git
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/tservers.js
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/tservers.js
index a06fa66090..03b477d41d 100644
---
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/tservers.js
+++
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/tservers.js
@@ -136,7 +136,7 @@ $(document).ready(function () {
// Create a table for tserver list
tserversTable = $('#tservers').DataTable({
"ajax": {
- "url": '/rest/tservers',
+ "url": contextPath + 'rest/tservers',
"dataSrc": "servers"
},
"stateSave": true,
@@ -195,7 +195,7 @@ $(document).ready(function () {
"type": "html",
"render": function (data, type, row) {
if (type === 'display') {
- data = '<a href="/tservers?s=' + row.id + '">' + row.hostname +
'</a>';
+ data = '<a href="tservers?s=' + row.id + '">' + row.hostname +
'</a>';
}
return data;
}
@@ -280,7 +280,7 @@ $(document).ready(function () {
// Create a table for deadServers list
deadTServersTable = $('#deadtservers').DataTable({
"ajax": {
- "url": '/rest/tservers',
+ "url": contextPath + 'rest/tservers',
"dataSrc": "deadServers"
},
"stateSave": true,
@@ -318,7 +318,7 @@ $(document).ready(function () {
// Create a table for badServers list
badTServersTable = $('#badtservers').DataTable({
"ajax": {
- "url": '/rest/tservers',
+ "url": contextPath + 'rest/tservers',
"dataSrc": "badServers"
},
"stateSave": true,
diff --git
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
index 19de384eba..037001569e 100644
---
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
@@ -21,6 +21,7 @@
<!DOCTYPE html>
<html>
<head>
+ <base href="${rootContext}"/>
<title>${title} - Accumulo ${version}</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<!-- external resources configurable by setting monitor.resources.external
-->
@@ -30,30 +31,30 @@
${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>
- <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" />
+ <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 -->
- <link rel="shortcut icon" type="image/jng"
href="/resources/images/favicon.png" />
- <script src="/resources/js/global.js"></script>
- <script src="/resources/js/functions.js"></script>
- <link rel="stylesheet" type="text/css" href="/resources/css/screen.css"
media="screen" />
+ <link rel="shortcut icon" type="image/jng"
href="resources/images/favicon.png" />
+ <script src="resources/js/global.js"></script>
+ <script src="resources/js/functions.js"></script>
+ <link rel="stylesheet" type="text/css" href="resources/css/screen.css"
media="screen" />
<script>
/**
@@ -64,10 +65,10 @@
});
</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/navbar.js"></script>
+ <script src="resources/js/systemAlert.js"></script>
</head>
<body>
diff --git
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/log.ftl
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/log.ftl
index cfa131cbc3..2c57623610 100644
---
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/log.ftl
+++
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/log.ftl
@@ -28,7 +28,7 @@
$(document).ready(function() {
logList = $('#logTable').DataTable( {
"ajax": {
- "url": '/rest/logs',
+ "url": '${rootContext}rest/logs',
"dataSrc": ""
},
"stateSave": true,
diff --git
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/modals.ftl
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/modals.ftl
index 471b5e9dd6..fb86006c4a 100644
---
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/modals.ftl
+++
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/modals.ftl
@@ -27,7 +27,7 @@
<div class="modal-header justify-content-center">
<span class="modal-title">
<div class="text-center">
- <a href="https://accumulo.apache.org" target="_blank"><img
alt="Apache Accumulo" src="/resources/images/accumulo-logo.png" /></a>
+ <a href="https://accumulo.apache.org" target="_blank"><img
alt="Apache Accumulo" src="resources/images/accumulo-logo.png" /></a>
</span>
</div>
</div>
diff --git
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
index 5b325c2114..7e77c4c3fe 100644
---
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
@@ -21,8 +21,8 @@
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<div class="navbar-header">
- <a class="navbar-brand" id="headertitle" style="text-decoration:
none" href="/">
- <img id="accumulo-avatar" alt="accumulo" class="navbar-left"
src="/resources/images/accumulo-avatar.png" />
+ <a class="navbar-brand" id="headertitle" style="text-decoration:
none" href="${rootContext}">
+ <img id="accumulo-avatar" alt="accumulo" class="navbar-left"
src="resources/images/accumulo-avatar.png" />
${instance_name}
</a>
<button class="navbar-toggler" type="button"
data-bs-toggle="collapse" data-bs-target="#nav-items" aria-controls="nav-items"
aria-expanded="false" aria-label="Toggle navigation">
@@ -39,24 +39,24 @@
<span id="statusNotification" class="icon-dot
normal"></span> Servers
</a>
<ul class="dropdown-menu">
- <li><a class="dropdown-item" href="/manager"><span
id="managerStatusNotification" class="icon-dot
normal"></span> Manager Server </a></li>
- <li><a class="dropdown-item" href="/tservers"><span
id="serverStatusNotification" class="icon-dot
normal"></span> Tablet Servers </a></li>
- <li><a class="dropdown-item" href="/gc"><span
id="gcStatusNotification" class="icon-dot
normal"></span> Garbage collector </a></li>
+ <li><a class="dropdown-item" href="manager"><span
id="managerStatusNotification" class="icon-dot
normal"></span> Manager Server </a></li>
+ <li><a class="dropdown-item" href="tservers"><span
id="serverStatusNotification" class="icon-dot
normal"></span> Tablet Servers </a></li>
+ <li><a class="dropdown-item" href="gc"><span
id="gcStatusNotification" class="icon-dot
normal"></span> Garbage collector </a></li>
</ul>
</li>
<li>
- <a class="nav-link" aria-current="page" href="/tables">Tables</a>
+ <a class="nav-link" aria-current="page" href="tables">Tables</a>
</li>
<li class="dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown"
role="button" data-bs-toggle="dropdown" aria-expanded="false">
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="/replication">Replication</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">
@@ -64,8 +64,8 @@
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>
+ <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">
@@ -73,8 +73,8 @@
role="button" data-bs-toggle="dropdown"
aria-expanded="false">REST
</a>
<ul class="dropdown-menu dropdown-menu-end">
- <li><a class="dropdown-item" href="/rest/xml">XML
Summary</a></li>
- <li><a class="dropdown-item" href="/rest/json">JSON
Summary</a></li>
+ <li><a class="dropdown-item" href="rest/xml">XML
Summary</a></li>
+ <li><a class="dropdown-item" href="rest/json">JSON
Summary</a></li>
</ul>
</li>
<li class="dropdown">
diff --git
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/overview.ftl
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/overview.ftl
index 1edcc570a6..70585ebdbf 100644
---
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/overview.ftl
+++
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/overview.ftl
@@ -28,21 +28,21 @@
<table class="table table-bordered table-striped table-condensed">
<thead>
<tr>
- <th colspan="2"><a href="/manager">Accumulo Manager</a></th>
+ <th colspan="2"><a href="manager">Accumulo Manager</a></th>
</tr>
<tr>
<td colspan="2" class="center" style="display:none;"><span
class="label label-danger nowrap">Manager is Down</span></td>
</tr>
<tr>
- <td class="left"><a href="/tables">Tables</a></td>
+ <td class="left"><a href="tables">Tables</a></td>
<td class="right"></td>
</tr>
<tr>
- <td class="left"><a
href="/tservers">Total Known Tablet Servers</a></td>
+ <td class="left"><a
href="tservers">Total Known Tablet Servers</a></td>
<td class="right"></td>
</tr>
<tr>
- <td class="left"><a
href="/tservers">Dead Tablet Servers</a></td>
+ <td class="left"><a
href="tservers">Dead Tablet Servers</a></td>
<td class="right"></td>
</tr>
<tr>
diff --git
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
index da1351aacc..c010c68dae 100644
---
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,7 +29,7 @@
tableList = $('#tableList').DataTable({
"ajax": {
- "url": "/rest/tables",
+ "url": '${rootContext}rest/tables',
"dataSrc": "table"
},
"stateSave": true,
@@ -79,7 +79,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>';
}
return data;
}
diff --git
a/server/monitor/src/test/java/org/apache/accumulo/monitor/EmbeddedWebServerTest.java
b/server/monitor/src/test/java/org/apache/accumulo/monitor/EmbeddedWebServerTest.java
new file mode 100644
index 0000000000..1b1cd90b0c
--- /dev/null
+++
b/server/monitor/src/test/java/org/apache/accumulo/monitor/EmbeddedWebServerTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.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();
+ expect(monitorMock.getConfiguration()).andReturn(config).atLeastOnce();
+
expect(monitorMock.getBindAddress()).andReturn("localhost:9995").atLeastOnce();
+
+ replay(contextMock, monitorMock);
+ monitor.set(monitorMock);
+ }
+
+ @AfterAll
+ public static void finishMocks() {
+ Monitor m = monitor.get();
+ verify(m.getContext(), m);
+ }
+
+ @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());
+ }
+}