Skip to content

Commit f61d933

Browse files
authored
Merge pull request #32 from evolvedbinary/6.x.x/hotfix/monex-monitoring-and-profiling
[6.x.x] Fix issues with Monitoring and Profiling as used by tools like Monex
2 parents 39df32e + 7473d61 commit f61d933

File tree

4 files changed

+93
-44
lines changed

4 files changed

+93
-44
lines changed

exist-core/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -914,7 +914,7 @@
914914
<include>src/test/java/org/exist/xquery/ForwardReferenceTest.java</include>
915915
<include>src/main/java/org/exist/xquery/FunctionFactory.java</include>
916916
<include>src/main/java/org/exist/xquery/Optimizer.java</include>
917-
<include>src/main/java/org/exist/xquery/PerformanceStatsImpl.java</include>
917+
<include>src/main/java/org/exist/xquery/PerformanceStats.java</include>
918918
<include>src/main/java/org/exist/xquery/TryCatchExpression.java</include>
919919
<include>src/main/java/org/exist/xquery/UserDefinedFunction.java</include>
920920
<include>src/main/java/org/exist/xquery/XPathUtil.java</include>
@@ -1256,7 +1256,7 @@
12561256
<exclude>src/main/java/org/exist/xquery/Materializable.java</exclude>
12571257
<exclude>src/main/java/org/exist/xquery/NameTest.java</exclude>
12581258
<exclude>src/main/java/org/exist/xquery/Optimizer.java</exclude>
1259-
<exclude>src/main/java/org/exist/xquery/PerformanceStatsImpl.java</exclude>
1259+
<exclude>src/main/java/org/exist/xquery/PerformanceStats.java</exclude>
12601260
<exclude>src/main/java/org/exist/xquery/TryCatchExpression.java</exclude>
12611261
<exclude>src/main/java/org/exist/xquery/UserDefinedFunction.java</exclude>
12621262
<exclude>src/test/java/org/exist/xquery/WatchdogTest.java</exclude>

exist-core/src/main/java/org/exist/management/client/JMXServlet.java

Lines changed: 54 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@
5656
import java.nio.file.Files;
5757
import java.nio.file.Path;
5858
import java.nio.file.Paths;
59+
import java.nio.file.attribute.PosixFileAttributes;
60+
import java.nio.file.attribute.PosixFilePermission;
61+
import java.nio.file.attribute.PosixFilePermissions;
5962
import java.util.HashSet;
6063
import java.util.Properties;
6164
import java.util.Set;
@@ -116,7 +119,7 @@ public class JMXServlet extends HttpServlet {
116119
}
117120

118121
private JMXtoXML client;
119-
private final Set<String> localhostAddresses = new HashSet<>();
122+
private final Set<String> serverAddresses = new HashSet<>();
120123

121124
private Path dataDir;
122125
private Path tokenFile;
@@ -128,11 +131,15 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t
128131
// Verify if request is from localhost or if user has specific servlet/container managed role.
129132
if (isFromLocalHost(request)) {
130133
// Localhost is always authorized to access
131-
LOG.debug("Local access granted");
134+
if (LOG.isDebugEnabled()) {
135+
LOG.debug("Local access granted");
136+
}
132137

133138
} else if (hasSecretToken(request, getToken())) {
134139
// Correct token is provided
135-
LOG.debug("Correct token provided by {}", request.getRemoteHost());
140+
if (LOG.isDebugEnabled()) {
141+
LOG.debug("Correct token provided by {}", request.getRemoteHost());
142+
}
136143

137144
} else {
138145
// Check if user is already authorized, e.g. via MONEX allow user too
@@ -218,7 +225,7 @@ public void init(ServletConfig config) throws ServletException {
218225
client.connect();
219226

220227
// Register all known localhost addresses
221-
registerLocalHostAddresses();
228+
registerServerAddresses();
222229

223230
// Get directory for token file
224231
final String jmxDataDir = client.getDataDir();
@@ -232,34 +239,47 @@ public void init(ServletConfig config) throws ServletException {
232239
}
233240

234241
// Setup token and tokenfile
235-
obtainTokenFileReference();
236-
237-
LOG.info("JMXservlet token: {}", getToken());
242+
if (tokenFile == null) {
243+
tokenFile = dataDir.resolve(TOKEN_FILE);
244+
LOG.info("Token file: {}", tokenFile.toAbsolutePath().toAbsolutePath());
245+
}
238246

247+
// NOTE(AR) make sure to create the token in init when the servlet is loaded at startup so that it is present for Monex
248+
final String token = getToken();
249+
LOG.info("JMXServlet token: {}", token);
239250
}
240251

241252
/**
242-
* Register all known IP-addresses for localhost.
253+
* Register all known IP-addresses for server.
243254
*/
244-
void registerLocalHostAddresses() {
245-
// The external IP address of the server
255+
void registerServerAddresses() {
256+
// The IPv4 address of the loopback interface of the server - 127.0.0.1 on Windows/Linux/macOS, or 127.0.1.1 on Debian/Ubuntu
257+
try {
258+
serverAddresses.add(InetAddress.getLocalHost().getHostAddress());
259+
} catch (final UnknownHostException ex) {
260+
LOG.warn("Unable to get loopback IP address for localhost: {}", ex.getMessage());
261+
}
262+
263+
// Any additional IPv4 and IPv6 addresses associated with the loopback interface of the server
246264
try {
247-
localhostAddresses.add(InetAddress.getLocalHost().getHostAddress());
248-
} catch (UnknownHostException ex) {
249-
LOG.warn("Unable to get HostAddress for localhost: {}", ex.getMessage());
265+
for (final InetAddress loopBackAddress : InetAddress.getAllByName("localhost")) {
266+
serverAddresses.add(loopBackAddress.getHostAddress());
267+
}
268+
} catch (final UnknownHostException ex) {
269+
LOG.warn("Unable to retrieve additional loopback IP addresses for localhost: {}", ex.getMessage());
250270
}
251271

252-
// The configured Localhost addresses
272+
// Any IPv4 and IPv6 addresses associated with other interfaces in the server
253273
try {
254-
for (InetAddress address : InetAddress.getAllByName("localhost")) {
255-
localhostAddresses.add(address.getHostAddress());
274+
for (final InetAddress hostAddress : InetAddress.getAllByName(InetAddress.getLocalHost().getHostName())) {
275+
serverAddresses.add(hostAddress.getHostAddress());
256276
}
257-
} catch (UnknownHostException ex) {
258-
LOG.warn("Unable to retrieve ipaddresses for localhost: {}", ex.getMessage());
277+
} catch (final UnknownHostException ex) {
278+
LOG.warn("Unable to retrieve additional interface IP addresses for localhost: {}", ex.getMessage());
259279
}
260280

261-
if (localhostAddresses.isEmpty()) {
262-
LOG.error("Unable to determine addresses for localhost, jmx servlet might be disfunctional.");
281+
if (serverAddresses.isEmpty()) {
282+
LOG.error("Unable to determine IP addresses for localhost, JMXServlet might be dysfunctional.");
263283
}
264284
}
265285

@@ -269,8 +289,13 @@ void registerLocalHostAddresses() {
269289
* @param request The HTTP request
270290
* @return TRUE if request is from LOCALHOST otherwise FALSE
271291
*/
272-
boolean isFromLocalHost(HttpServletRequest request) {
273-
return localhostAddresses.contains(request.getRemoteAddr());
292+
boolean isFromLocalHost(final HttpServletRequest request) {
293+
String remoteAddr = request.getRemoteAddr();
294+
if (remoteAddr.charAt(0) == '[') {
295+
// Handle IPv6 addresses that are wrapped in []
296+
remoteAddr = remoteAddr.substring(1, remoteAddr.length() - 1);
297+
}
298+
return serverAddresses.contains(remoteAddr);
274299
}
275300

276301
/**
@@ -291,17 +316,6 @@ boolean hasSecretToken(HttpServletRequest request, String token) {
291316
return false;
292317
}
293318

294-
/**
295-
* Obtain reference to token file
296-
*/
297-
private void obtainTokenFileReference() {
298-
299-
if (tokenFile == null) {
300-
tokenFile = dataDir.resolve(TOKEN_FILE);
301-
LOG.info("Token file: {}", tokenFile.toAbsolutePath().toAbsolutePath());
302-
}
303-
}
304-
305319
/**
306320
* Get token from file, create if not existent. Data is read for each call so the file can be updated run-time.
307321
*
@@ -327,6 +341,13 @@ private String getToken() {
327341
// Create and write when needed
328342
if (!Files.exists(tokenFile) || token == null) {
329343

344+
final Set<PosixFilePermission> permissions = PosixFilePermissions.fromString("rw-r-----");
345+
try {
346+
tokenFile = Files.createFile(tokenFile, PosixFilePermissions.asFileAttribute(permissions));
347+
} catch (final Throwable t) {
348+
LOG.warn("Unable to restrict permissions on: " + tokenFile);
349+
}
350+
330351
// Create random token
331352
token = UUIDGenerator.getUUIDversion4();
332353

exist-core/src/main/java/org/exist/xquery/PerformanceStats.java

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,28 @@
11
/*
2+
* Elemental
3+
* Copyright (C) 2024, Evolved Binary Ltd
4+
*
5+
6+
* https://www.evolvedbinary.com | https://www.elemental.xyz
7+
*
8+
* This library is free software; you can redistribute it and/or
9+
* modify it under the terms of the GNU Lesser General Public
10+
* License as published by the Free Software Foundation; version 2.1.
11+
*
12+
* This library is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
* Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public
18+
* License along with this library; if not, write to the Free Software
19+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20+
*
21+
* NOTE: Parts of this file contain code from 'The eXist-db Authors'.
22+
* The original license header is included below.
23+
*
24+
* =====================================================================
25+
*
226
* eXist-db Open Source Native XML Database
327
* Copyright (C) 2001 The eXist-db Authors
428
*
@@ -19,7 +43,6 @@
1943
* License along with this library; if not, write to the Free Software
2044
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2145
*/
22-
2346
package org.exist.xquery;
2447

2548
import org.apache.commons.io.output.StringBuilderWriter;
@@ -317,9 +340,10 @@ private FunctionStats[] sort() {
317340
return stats;
318341
}
319342

320-
public synchronized void toXML(MemTreeBuilder builder) {
343+
public synchronized void toXML(final MemTreeBuilder builder) {
321344
final AttributesImpl attrs = new AttributesImpl();
322-
builder.startElement(new QName("calls", XML_NAMESPACE, XML_PREFIX), null);
345+
attrs.addAttribute("", "tracing-enabled", "tracing-enabled", "CDATA", Boolean.toString(isEnabled()));
346+
builder.startElement(new QName("calls", XML_NAMESPACE, XML_PREFIX), attrs);
323347
for (final QueryStats stats : queries.values()) {
324348
attrs.clear();
325349
attrs.addAttribute("", "source", "source", "CDATA", stats.source);
@@ -333,20 +357,19 @@ public synchronized void toXML(MemTreeBuilder builder) {
333357
attrs.addAttribute("", "name", "name", "CDATA", stats.qname.getStringValue());
334358
attrs.addAttribute("", "elapsed", "elapsed", "CDATA", Double.toString(stats.executionTime / 1000.0));
335359
attrs.addAttribute("", "calls", "calls", "CDATA", Integer.toString(stats.callCount));
336-
if (stats.source != null)
337-
{attrs.addAttribute("", "source", "source", "CDATA", stats.source);}
360+
if (stats.source != null) {
361+
attrs.addAttribute("", "source", "source", "CDATA", stats.source);
362+
}
338363
builder.startElement(new QName("function", XML_NAMESPACE, XML_PREFIX), attrs);
339364
builder.endElement();
340365
}
341366
for (final IndexStats stats: indexStats.values()) {
342367
attrs.clear();
343368
attrs.addAttribute("", "type", "type", "CDATA", stats.indexType);
344-
attrs.addAttribute("", "source", "source", "CDATA", stats.source + " [" + stats.line + ":" +
345-
stats.column + "]");
369+
attrs.addAttribute("", "source", "source", "CDATA", stats.source + " [" + stats.line + ":" + stats.column + "]");
346370
attrs.addAttribute("", "elapsed", "elapsed", "CDATA", Double.toString(stats.executionTime / 1000.0));
347371
attrs.addAttribute("", "calls", "calls", "CDATA", Integer.toString(stats.usageCount));
348-
attrs.addAttribute("", "optimization", "optimization", "CDATA",
349-
Integer.toString(stats.mode));
372+
attrs.addAttribute("", "optimization", "optimization", "CDATA", Integer.toString(stats.mode));
350373
builder.startElement(new QName("index", XML_NAMESPACE, XML_PREFIX), attrs);
351374
builder.endElement();
352375
}

exist-jetty-config/src/main/resources/webapp/WEB-INF/web.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,11 @@
176176
<servlet>
177177
<servlet-name>JMXServlet</servlet-name>
178178
<servlet-class>org.exist.management.client.JMXServlet</servlet-class>
179+
<!-- NOTE(AR) required for Monex to correctly retrieve the JMX Token
180+
as the JMXServlet has to create it before Monex makes its first request
181+
for the status page
182+
-->
183+
<load-on-startup>3</load-on-startup>
179184
</servlet>
180185

181186
<!--

0 commit comments

Comments
 (0)