diff --git a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/util/OSUtils.java b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/util/OSUtils.java index 95df4b3bc8..cde0bafe23 100644 --- a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/util/OSUtils.java +++ b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/util/OSUtils.java @@ -31,62 +31,6 @@ import org.slf4j.Logger; * OS specific utilities with generic method interfaces */ public final class OSUtils { - /** - * @param process NiFi Process Reference - * @param logger Logger Reference for Debug - * @return Returns pid or null in-case pid could not be determined - * This method takes {@link Process} and {@link Logger} and returns - * the platform specific ProcessId for Unix like systems, a.k.a pid - * In-case it fails to determine the pid, it will return Null. - * Purpose for the Logger is to log any interaction for debugging. - */ - private static Long getUnicesPid(final Process process, final Logger logger) { - try { - final Class procClass = process.getClass(); - final Field pidField = procClass.getDeclaredField("pid"); - pidField.setAccessible(true); - final Object pidObject = pidField.get(process); - - logger.debug("PID Object = {}", pidObject); - - if (pidObject instanceof Number) { - return ((Number) pidObject).longValue(); - } - return null; - } catch (final IllegalAccessException | NoSuchFieldException nsfe) { - logger.debug("Could not find PID for child process due to {}", nsfe); - return null; - } - } - - /** - * @param process NiFi Process Reference - * @param logger Logger Reference for Debug - * @return Returns pid or null in-case pid could not be determined - * This method takes {@link Process} and {@link Logger} and returns - * the platform specific Handle for Win32 Systems, a.k.a pid - * In-case it fails to determine the pid, it will return Null. - * Purpose for the Logger is to log any interaction for debugging. - */ - private static Long getWindowsProcessId(final Process process, final Logger logger) { - /* determine the pid on windows plattforms */ - try { - Field f = process.getClass().getDeclaredField("handle"); - f.setAccessible(true); - long handl = f.getLong(process); - - Kernel32 kernel = Kernel32.INSTANCE; - WinNT.HANDLE handle = new WinNT.HANDLE(); - handle.setPointer(Pointer.createConstant(handl)); - int ret = kernel.GetProcessId(handle); - logger.debug("Detected pid: {}", ret); - return Long.valueOf(ret); - } catch (final IllegalAccessException | NoSuchFieldException nsfe) { - logger.debug("Could not find PID for child process due to {}", nsfe); - } - return null; - } - /** * @param process NiFi Process Reference * @param logger Logger Reference for Debug @@ -109,49 +53,35 @@ public final class OSUtils { * of the pid method to the Process API. */ Long pid = null; - if (!System.getProperty("java.version").startsWith("1.")) { - try { - Method pidMethod = process.getClass().getMethod("pid"); - pidMethod.setAccessible(true); - Object pidMethodResult = pidMethod.invoke(process); - if (Long.class.isAssignableFrom(pidMethodResult.getClass())) { - pid = (Long) pidMethodResult; - } else { - logger.debug("Could not determine PID for child process because returned PID was not " + - "assignable to type " + Long.class.getName()); - } - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - logger.debug("Could not find PID for child process due to {}", e); + try { + // Get Process.pid() interface method to avoid illegal reflective access + final Method pidMethod = Process.class.getDeclaredMethod("pid"); + final Object pidNumber = pidMethod.invoke(process); + if (pidNumber instanceof Long) { + pid = (Long) pidNumber; + } + } catch (final NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + final String processClassName = process.getClass().getName(); + if (processClassName.equals("java.lang.UNIXProcess")) { + pid = getUnixPid(process, logger); + } else if (processClassName.equals("java.lang.Win32Process") + || processClassName.equals("java.lang.ProcessImpl")) { + pid = getWindowsProcessId(process, logger); + } else { + logger.debug("Failed to determine Process ID from [{}]: {}", processClassName, e.getMessage()); } - } else if (process.getClass().getName().equals("java.lang.UNIXProcess")) { - pid = getUnicesPid(process, logger); - } else if (process.getClass().getName().equals("java.lang.Win32Process") - || process.getClass().getName().equals("java.lang.ProcessImpl")) { - pid = getWindowsProcessId(process, logger); } return pid; } - // The two Java version methods are copied from CertificateUtils in nifi-commons/nifi-security-utils - - /** - * Returns the JVM Java major version based on the System properties (e.g. {@code JVM 1.8.0.231} -> {code 8}). - * - * @return the Java major version - */ - public static int getJavaVersion() { - String version = System.getProperty("java.version"); - return parseJavaVersion(version); - } - /** * Returns the major version parsed from the provided Java version string (e.g. {@code "1.8.0.231"} -> {@code 8}). * * @param version the Java version string * @return the major version as an int */ - public static int parseJavaVersion(String version) { + public static int parseJavaVersion(final String version) { String majorVersion; if (version.startsWith("1.")) { majorVersion = version.substring(2, 3); @@ -167,4 +97,55 @@ public final class OSUtils { return Integer.parseInt(majorVersion); } + /** + * @param process NiFi Process Reference + * @param logger Logger Reference for Debug + * @return Returns pid or null in-case pid could not be determined + * This method takes {@link Process} and {@link Logger} and returns + * the platform specific ProcessId for Unix like systems, a.k.a pid + * In-case it fails to determine the pid, it will return Null. + * Purpose for the Logger is to log any interaction for debugging. + */ + private static Long getUnixPid(final Process process, final Logger logger) { + try { + final Class procClass = process.getClass(); + final Field pidField = procClass.getDeclaredField("pid"); + pidField.setAccessible(true); + final Object pidObject = pidField.get(process); + + if (pidObject instanceof Number) { + return ((Number) pidObject).longValue(); + } + return null; + } catch (final IllegalAccessException | NoSuchFieldException e) { + logger.debug("Could not find Unix PID", e); + return null; + } + } + + /** + * @param process NiFi Process Reference + * @param logger Logger Reference for Debug + * @return Returns pid or null in-case pid could not be determined + * This method takes {@link Process} and {@link Logger} and returns + * the platform specific Handle for Win32 Systems, a.k.a pid + * In-case it fails to determine the pid, it will return Null. + * Purpose for the Logger is to log any interaction for debugging. + */ + private static Long getWindowsProcessId(final Process process, final Logger logger) { + Long pid = null; + try { + final Field handleField = process.getClass().getDeclaredField("handle"); + handleField.setAccessible(true); + long peer = handleField.getLong(process); + + final Kernel32 kernel = Kernel32.INSTANCE; + final WinNT.HANDLE handle = new WinNT.HANDLE(); + handle.setPointer(Pointer.createConstant(peer)); + pid = Long.valueOf(kernel.GetProcessId(handle)); + } catch (final IllegalAccessException | NoSuchFieldException e) { + logger.debug("Could not find Windows PID", e); + } + return pid; + } } diff --git a/nifi-bootstrap/src/test/groovy/org/apache/nifi/bootstrap/util/OSUtilsTest.groovy b/nifi-bootstrap/src/test/groovy/org/apache/nifi/bootstrap/util/OSUtilsTest.groovy deleted file mode 100644 index 7facaa9b0e..0000000000 --- a/nifi-bootstrap/src/test/groovy/org/apache/nifi/bootstrap/util/OSUtilsTest.groovy +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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 - * - * http://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.nifi.bootstrap.util - - -import org.junit.After -import org.junit.AfterClass -import org.junit.Before -import org.junit.BeforeClass -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 -import org.slf4j.Logger -import org.slf4j.LoggerFactory - -@RunWith(JUnit4.class) -class OSUtilsTest extends GroovyTestCase { - private static final Logger logger = LoggerFactory.getLogger(OSUtilsTest.class) - - @BeforeClass - static void setUpOnce() throws Exception { - logger.metaClass.methodMissing = { String name, args -> - logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}") - } - } - - @AfterClass - static void tearDownOnce() throws Exception { - - } - - @Before - void setUp() throws Exception { - - } - - @After - void tearDown() throws Exception { - - } - - @Test - void testShouldParseJavaMajorVersion8Below() { - // Arrange - def possibleVersionStrings = ["1.8", "1.8.0.0", "1.8.0_262"] - - // Act - def results = possibleVersionStrings.collect { - OSUtils.parseJavaVersion(it) - } - logger.info("Parsed Java versions: ${results}") - - // Assert - assert results.every { it == 8 } - } - - @Test - void testShouldParseJavaMajorVersion9Plus() { - // Arrange - def possibleVersionStrings = [ - "11.0.6", "11.0.0", "11.12.13", "11" - ] - - // Act - def results = possibleVersionStrings.collect { - OSUtils.parseJavaVersion(it) - } - logger.info("Parsed Java versions: ${results}") - - // Assert - assert results.every { it == 11 } - } -} diff --git a/nifi-bootstrap/src/test/java/org/apache/nifi/bootstrap/http/OSUtilsTest.java b/nifi-bootstrap/src/test/java/org/apache/nifi/bootstrap/http/OSUtilsTest.java new file mode 100644 index 0000000000..89a9dbc6ba --- /dev/null +++ b/nifi-bootstrap/src/test/java/org/apache/nifi/bootstrap/http/OSUtilsTest.java @@ -0,0 +1,56 @@ +/* + * 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 + * + * http://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.nifi.bootstrap.http; + +import org.apache.nifi.bootstrap.util.OSUtils; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class OSUtilsTest { + + @Test + public void testGetPid() throws IOException { + final ProcessBuilder builder = new ProcessBuilder(); + final Process process = builder.command("java").start(); + final Logger logger = LoggerFactory.getLogger("testing"); + final Long pid = OSUtils.getProcessId(process, logger); + process.destroy(); + assertNotNull("Process ID not found", pid); + } + + @Test + public void testParseJavaVersion8() { + final String[] versions = new String[] { "1.8", "1.8.0", "1.8.0_100" }; + for (final String version : versions) { + assertEquals(8, OSUtils.parseJavaVersion(version)); + } + } + + @Test + public void testParseJavaVersion11() { + final String[] versions = new String[] { "11", "11.0", "11.0.11" }; + for (final String version : versions) { + assertEquals(11, OSUtils.parseJavaVersion(version)); + } + } +}