blob: d519d3fb4ec2dd4fbf382aa365235826517e4c8b [file] [log] [blame]
/*
* Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.jersey.test.memleak.common;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.management.MBeanServer;
/**
* Utility class for memory leak test infrastructure.
*
* @author Stepan Vavra
*/
public class MemoryLeakUtils {
/**
* The log file where the output (stdout/stderr) of container is located.
* <p/>
* For instance, this file is used for detection of {@link OutOfMemoryError} exception records.
*/
public static final String JERSEY_CONFIG_TEST_CONTAINER_LOGFILE = "jersey.config.test.container.logfile";
/**
* The memory leak timeout denotes successful end of the memory leak test. That is, if the memory leak didn't occur during the
* specified timeout, the test successfully finishes.
*/
public static final String JERSEY_CONFIG_TEST_MEMLEAK_TIMEOUT = "jersey.config.test.memleak.timeout";
/**
* The context root where the deployed application will be accessible.
*/
public static final String JERSEY_CONFIG_TEST_CONTAINER_CONTEXT_ROOT = "jersey.config.test.container.contextRoot";
/**
* The path where to create heap dump files.
*/
public static final String JERSEY_CONFIG_TEST_MEMLEAK_HEAP_DUMP_PATH = "jersey.config.test.memleak.heapDumpPath";
private MemoryLeakUtils() {
}
private static final Pattern PATTERN = Pattern.compile(".*java\\.lang\\.OutOfMemoryError.*");
/**
* Scans the file denoted by {@link #JERSEY_CONFIG_TEST_CONTAINER_LOGFILE} for {@link OutOfMemoryError} records.
*
* @throws IOException In case of I/O error.
* @throws IllegalStateException In case the {@link OutOfMemoryError} record was found.
*/
public static void verifyNoOutOfMemoryOccurred() throws IOException {
final String logFileName = System.getProperty(JERSEY_CONFIG_TEST_CONTAINER_LOGFILE);
System.out.println("Verifying whether OutOfMemoryError occurred in log file: " + logFileName);
if (logFileName == null) {
return;
}
final File logFile = new File(logFileName);
if (!logFile.exists()) {
return;
}
final List<String> lines = Files.lines(logFile.toPath(), Charset.defaultCharset())
.filter(line -> PATTERN.matcher(line).matches()).collect(Collectors.toList());
if (lines.size() > 0) {
throw new IllegalStateException(
"OutOfMemoryError detected in '" + logFileName + "': " + Arrays.toString(lines.toArray()));
}
}
/**
* The name of the HotSpot Diagnostic MXBean
*/
private static final String HOTSPOT_BEAN_NAME = "com.sun.management:type=HotSpotDiagnostic";
/**
* The class name of HotSpot Diagnostic MXBean
*/
private static final String HOT_SPOT_DIAGNOSTIC_MXBEAN_CLASSNAME = "com.sun.management.HotSpotDiagnosticMXBean";
/**
* Hotspot diagnostic MBean singleton
*/
private static volatile Object hotSpotDiagnosticMBean;
private static volatile Method dumpHeapMethod;
/**
* Create a heap dump into a given file.
*
* @param fileName name of the heap dump file
* @param live whether to dump only the live objects
*/
static void dumpHeap(String fileName, boolean live)
throws InvocationTargetException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, IOException {
conditionallyInitHotSpotDiagnosticMXBean();
try {
java.nio.file.Files.deleteIfExists(Paths.get(fileName));
} catch (IOException e) {
// do nothing and try to go further
}
dumpHeapMethod.invoke(hotSpotDiagnosticMBean, fileName, live);
}
/**
* Initialize the HotSpot diagnostic MBean
*/
private static void conditionallyInitHotSpotDiagnosticMXBean()
throws IOException, ClassNotFoundException, NoSuchMethodException {
if (hotSpotDiagnosticMBean == null) {
synchronized (MemoryLeakUtils.class) {
if (hotSpotDiagnosticMBean == null) {
Class clazz = Class.forName(HOT_SPOT_DIAGNOSTIC_MXBEAN_CLASSNAME);
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
hotSpotDiagnosticMBean = ManagementFactory.newPlatformMXBeanProxy(server, HOTSPOT_BEAN_NAME, clazz);
dumpHeapMethod = clazz.getMethod("dumpHeap", String.class, boolean.class);
}
}
}
}
}