diff --git a/.github/workflows/push_pr.yml b/.github/workflows/push_pr.yml new file mode 100644 index 00000000..74aff5c7 --- /dev/null +++ b/.github/workflows/push_pr.yml @@ -0,0 +1,40 @@ +--- +name: Java CI - PR + +on: [ pull_request ] + +env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + +jobs: + + test: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: '11' + java-package: jdk + - name: Build with Gradle + run: ./gradlew build --info --stacktrace + - name: Package with Gradle + run: ./gradlew package --warn --stacktrace + - name: Push bin to GH workflow artifacts cache + uses: actions/upload-artifact@v2 + with: + name: dist-linux + path: build/distributions + + snyk: + name: Run security checks via snyk + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - name: Run Snyk to check for vulnerabilities + uses: snyk/actions/gradle-jdk11@master + env: + SNYK_TOKEN: ${{env.SNYK_TOKEN}} + with: + args: --severity-threshold=high diff --git a/.github/workflows/repolinter.yml b/.github/workflows/repolinter.yml new file mode 100644 index 00000000..acb921f3 --- /dev/null +++ b/.github/workflows/repolinter.yml @@ -0,0 +1,31 @@ +# NOTE: This file should always be named `repolinter.yml` to allow +# workflow_dispatch to work properly +name: Repolinter Action + +# NOTE: This workflow will ONLY check the default branch! +# Currently there is no elegant way to specify the default +# branch in the event filtering, so branches are instead +# filtered in the "Test Default Branch" step. +on: [push, workflow_dispatch] + +jobs: + repolint: + name: Run Repolinter + runs-on: ubuntu-latest + steps: + - name: Test Default Branch + id: default-branch + uses: actions/github-script@v2 + with: + script: | + const data = await github.repos.get(context.repo) + return data.data && data.data.default_branch === context.ref.split('/').slice(-1)[0] + - name: Checkout Self + if: ${{ steps.default-branch.outputs.result == 'true' }} + uses: actions/checkout@v2 + - name: Run Repolinter + if: ${{ steps.default-branch.outputs.result == 'true' }} + uses: newrelic/repolinter-action@v1 + with: + config_url: https://raw.githubusercontent.com/newrelic/.github/main/repolinter-rulesets/community-plus.yml + output_type: issue diff --git a/build.gradle.kts b/build.gradle.kts index 6df851eb..d46a0aff 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ plugins { `maven-publish` id("org.beryx.jlink") version ("2.21.2") id("org.ysb33r.java.modulehelper") version ("0.9.0") - id("com.github.sherter.google-java-format") version ("0.8") + id("com.github.sherter.google-java-format") version ("0.9") id("nebula.ospackage") version ("8.4.1") id("fi.linuxbox.download") version ("0.6") } @@ -34,7 +34,7 @@ extraJavaModules { module("commons-cli-1.4.jar", "commons.cli", "1.4") { exports("org.apache.commons.cli") } - module("gson-2.8.0.jar", "com.google.code.gson", "2.8.0") { + module("gson-2.8.0.jar", "com.google.code.gson", "2.8.6") { exports("com.google.gson") } } @@ -86,6 +86,20 @@ tasks.register("jmxtermScripts") { (windowsStartScriptGenerator as TemplateBasedScriptGenerator).template = project.resources.text.fromFile(file("src/jmxterm/jmxterm.template.bat")) } +tasks.register("noarchJar") { + dependsOn(configurations.runtimeClasspath) + destinationDirectory.set(file("${buildDir}/distributions")) + archiveClassifier.set("noarch") + + manifest.attributes("Main-Class" to "org.newrelic.nrjmx.Application") + + from(sourceSets.main.get().output) + + from({ + configurations.runtimeClasspath.get().filter { it.name.endsWith("jar") }.map { zipTree(it) } + }) +} + tasks.register("jlinkDistZip") { dependsOn(tasks.jlink, "downloadJmxTerm", "jmxtermScripts") destinationDirectory.set(file("${buildDir}/distributions")) @@ -102,7 +116,7 @@ tasks.register("jlinkDistZip") { into("lib") } from("${buildDir}/jmxterm/bin") { - into ("bin") + into("bin") fileMode = 0x1ED } } @@ -124,7 +138,7 @@ tasks.register("jlinkDistTar") { into("lib") } from("${buildDir}/jmxterm/bin") { - into ("bin") + into("bin") fileMode = 0x1ED } } @@ -208,5 +222,12 @@ tasks.distTar { tasks.register("package") { group = "Distribution" description = "Builds all packages" - dependsOn("distTar", "distZip", "buildDeb", "buildRpm", "jlinkDistZip","jlinkDistTar") -} \ No newline at end of file + dependsOn( + "noarchJar", + "distTar", + "distZip", + "buildDeb", + "buildRpm", + "jlinkDistZip", + "jlinkDistTar") +} diff --git a/src/main/java/org/newrelic/nrjmx/Application.java b/src/main/java/org/newrelic/nrjmx/Application.java index d7f72a73..b1e92b82 100644 --- a/src/main/java/org/newrelic/nrjmx/Application.java +++ b/src/main/java/org/newrelic/nrjmx/Application.java @@ -9,6 +9,7 @@ import org.apache.commons.cli.HelpFormatter; public class Application { + public static void printHelp() { new HelpFormatter().printHelp("nrjmx", Arguments.options()); } diff --git a/src/main/java/org/newrelic/nrjmx/Arguments.java b/src/main/java/org/newrelic/nrjmx/Arguments.java index cf75d6ed..fdbadb9c 100644 --- a/src/main/java/org/newrelic/nrjmx/Arguments.java +++ b/src/main/java/org/newrelic/nrjmx/Arguments.java @@ -5,9 +5,15 @@ package org.newrelic.nrjmx; -import org.apache.commons.cli.*; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; class Arguments { + + private static Options options = null; private String hostname; private String connectionURL; private int port; @@ -24,8 +30,6 @@ class Arguments { private boolean isRemoteJBossStandalone; private boolean help; - private static Options options = null; - private Arguments() {} static Options options() { diff --git a/src/main/java/org/newrelic/nrjmx/JMXFetcher.java b/src/main/java/org/newrelic/nrjmx/JMXFetcher.java index 7e279132..d12f4bdf 100644 --- a/src/main/java/org/newrelic/nrjmx/JMXFetcher.java +++ b/src/main/java/org/newrelic/nrjmx/JMXFetcher.java @@ -39,6 +39,7 @@ * OutputStream (usually stdout) */ public class JMXFetcher { + public static final String defaultURIPath = "jmxrmi"; public static final Boolean defaultJBossModeIsStandalone = false; @@ -49,24 +50,6 @@ public class JMXFetcher { private String connectionString; private Map connectionEnv = new HashMap<>(); - public class ConnectionError extends Exception { - public ConnectionError(String message, Exception cause) { - super(message, cause); - } - } - - public class QueryError extends Exception { - public QueryError(String message, Exception cause) { - super(message, cause); - } - } - - public class ValueError extends Exception { - public ValueError(String message) { - super(message); - } - } - /** * Builds a new JMXFetcher with user & pass from a connection URL. * @@ -383,7 +366,9 @@ private void parseValue(String name, Object value) throws ValueError { Set fieldKeys = cdata.getCompositeType().keySet(); for (String field : fieldKeys) { - if (field.length() < 1) continue; + if (field.length() < 1) { + continue; + } String fieldKey = field.substring(0, 1).toUpperCase() + field.substring(1); parseValue(String.format("%s.%s", name, fieldKey), cdata.get(field)); @@ -430,4 +415,25 @@ private Float parseFloat(Float value) { return value; } + + public class ConnectionError extends Exception { + + public ConnectionError(String message, Exception cause) { + super(message, cause); + } + } + + public class QueryError extends Exception { + + public QueryError(String message, Exception cause) { + super(message, cause); + } + } + + public class ValueError extends Exception { + + public ValueError(String message) { + super(message); + } + } } diff --git a/src/main/java/org/newrelic/nrjmx/Logging.java b/src/main/java/org/newrelic/nrjmx/Logging.java index ced88e4f..5ea66151 100644 --- a/src/main/java/org/newrelic/nrjmx/Logging.java +++ b/src/main/java/org/newrelic/nrjmx/Logging.java @@ -5,9 +5,14 @@ package org.newrelic.nrjmx; -import java.util.logging.*; +import java.util.logging.ConsoleHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.logging.SimpleFormatter; public class Logging { + public static void setup(Logger logger, boolean verbose) { logger.setUseParentHandlers(false); Handler consoleHandler = new ConsoleHandler(); diff --git a/test-nrjmx/src/test/java/org/newrelic/jmx/CatsClient.java b/test-nrjmx/src/test/java/org/newrelic/jmx/CatsClient.java index 83105d45..3c5f3acc 100644 --- a/test-nrjmx/src/test/java/org/newrelic/jmx/CatsClient.java +++ b/test-nrjmx/src/test/java/org/newrelic/jmx/CatsClient.java @@ -5,18 +5,23 @@ package org.newrelic.jmx; -import org.testcontainers.shaded.okhttp3.*; +import org.testcontainers.shaded.okhttp3.FormBody; +import org.testcontainers.shaded.okhttp3.MediaType; +import org.testcontainers.shaded.okhttp3.OkHttpClient; +import org.testcontainers.shaded.okhttp3.Request; +import org.testcontainers.shaded.okhttp3.RequestBody; +import org.testcontainers.shaded.okhttp3.Response; public class CatsClient { private String baseURL; private OkHttpClient client = new OkHttpClient(); - public CatsClient(String baseURL) { + public CatsClient(final String baseURL) { this.baseURL = baseURL; } - public String add(String catName) { + public String add(final String catName) { RequestBody catBody = FormBody.create(MediaType.parse("application/json"), "{\"name\":\"" + catName + "\"}"); diff --git a/test-nrjmx/src/test/java/org/newrelic/jmx/JMXFetcherTest.java b/test-nrjmx/src/test/java/org/newrelic/jmx/JMXFetcherTest.java index dbc9928b..ca0ae9fc 100644 --- a/test-nrjmx/src/test/java/org/newrelic/jmx/JMXFetcherTest.java +++ b/test-nrjmx/src/test/java/org/newrelic/jmx/JMXFetcherTest.java @@ -8,8 +8,14 @@ import static java.util.concurrent.TimeUnit.SECONDS; import static org.junit.jupiter.api.Assertions.assertEquals; -import java.io.*; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStreamReader; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; import java.util.Arrays; +import java.util.Collections; import java.util.Locale; import java.util.logging.Logger; import org.junit.jupiter.api.Timeout; @@ -23,16 +29,83 @@ import org.testcontainers.images.builder.ImageFromDockerfile; public class JMXFetcherTest { + + // Runs the JMX-monitored test container without SSL enabled + private static GenericContainer jmxService(String jdkName) { + GenericContainer container = + getContainerFromDockerfile(jdkName) + .withExposedPorts(4567, 7199) + .withEnv( + "JAVA_OPTS", + "-Dcom.sun.management.jmxremote.port=7199 " + + "-Dcom.sun.management.jmxremote.rmi.port=7199 " + + "-Djava.rmi.server.hostname=localhost " + + "-Dcom.sun.management.jmxremote=true " + + "-Dcom.sun.management.jmxremote.authenticate=false " + + "-Dcom.sun.management.jmxremote.ssl=false "); + container.setPortBindings(Arrays.asList("7199:7199", "4567:4567")); + Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LoggerFactory.getLogger("TESTCONT")); + container.setLogConsumers(Collections.singletonList(logConsumer)); + return container; + } + + // Runs the JMX-monitored test container with SSL enabled + private static GenericContainer jmxSSLService(String jdkName) { + GenericContainer container = + getContainerFromDockerfile(jdkName) + .withExposedPorts(4567, 7199) + .withEnv( + "JAVA_OPTS", + "-Dcom.sun.management.jmxremote.port=7199 " + + "-Dcom.sun.management.jmxremote.rmi.port=7199 " + + "-Djava.rmi.server.hostname=localhost " + + "-Dcom.sun.management.jmxremote=true " + + "-Dcom.sun.management.jmxremote.authenticate=false " + + "-Dcom.sun.management.jmxremote.ssl=true " + + "-Dcom.sun.management.jmxremote.ssl.need.client.auth=true " + + "-Dcom.sun.management.jmxremote.registry.ssl=true " + + "-Djavax.net.ssl.keyStore=/serverkeystore " + + "-Djavax.net.ssl.keyStorePassword=serverpass " + + "-Djavax.net.ssl.trustStore=/servertruststore " + + "-Djavax.net.ssl.trustStorePassword=servertrustpass"); + container.setPortBindings(Arrays.asList("7199:7199", "4567:4567")); + Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LoggerFactory.getLogger("TESTCONT")); + container.setLogConsumers(Collections.singletonList(logConsumer)); + return container; + } + + private static GenericContainer getContainerFromDockerfile(String jdkName) { + String sysProp = System.getProperty("TEST_SERVER_" + jdkName.toUpperCase(Locale.US)); + return new GenericContainer<>( + new ImageFromDockerfile().withFileFromFile(".", new File(sysProp))); + // .withFileFromFile( + // "bin", new File(System.getProperty("TEST_SERVER_DOCKER_FILES"), "bin")) + // .withFileFromFile( + // "lib", new File(System.getProperty("TEST_SERVER_DOCKER_FILES"), "lib"))); + } + + private static void eventually(long timeoutMs, Runnable r) throws Exception { + long timeoutNano = timeoutMs * 1_000_000; + long startTime = System.nanoTime(); + Exception lastException = null; + while (System.nanoTime() - timeoutNano < startTime) { + try { + r.run(); + return; + } catch (Exception e) { + lastException = e; + } + } + throw lastException; + } + @Timeout(value = 60, unit = SECONDS) @ParameterizedTest @ValueSource(strings = {"jdk11", "jdk8"}) public void testJMX(String jdkName) throws Exception { - GenericContainer container = jmxService(jdkName); - try { + try (GenericContainer container = jmxService(jdkName)) { container.start(); testJMXFetching(new JMXFetcher("localhost", 7199, "", "", "", "", "", "", false)); - } finally { - container.close(); } } @@ -40,14 +113,11 @@ public void testJMX(String jdkName) throws Exception { @ParameterizedTest @ValueSource(strings = {"jdk11", "jdk8"}) public void testJMXFromConnectionURL(String jdkName) throws Exception { - GenericContainer container = jmxService(jdkName); - try { + try (GenericContainer container = jmxService(jdkName)) { container.start(); testJMXFetching( new JMXFetcher( "service:jmx:rmi:///jndi/rmi://localhost:7199/jmxrmi", "", "", "", "", "", "")); - } finally { - container.close(); } } @@ -55,12 +125,8 @@ public void testJMXFromConnectionURL(String jdkName) throws Exception { @ParameterizedTest @ValueSource(strings = {"jdk11", "jdk8"}) public void testJMXWithSSL(String jdkName) throws Exception { - GenericContainer container = jmxSSLService(jdkName); - try { - Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LoggerFactory.getLogger("TESTCONT")); - + try (GenericContainer container = jmxSSLService(jdkName)) { container.start(); - container.followOutput(logConsumer); testJMXFetching( new JMXFetcher( "localhost", @@ -72,8 +138,6 @@ public void testJMXWithSSL(String jdkName) throws Exception { getClass().getResource("/clienttruststore").getPath(), "clienttrustpass", false)); - } finally { - container.close(); } } @@ -135,69 +199,4 @@ public void run() { results.close(); } - - // Runs the JMX-monitored test container without SSL enabled - private static GenericContainer jmxService(String jdkName) { - GenericContainer container = - getContainerFromDockerfile(jdkName) - .withExposedPorts(4567, 7199) - .withEnv( - "JAVA_OPTS", - "-Dcom.sun.management.jmxremote.port=7199 " - + "-Dcom.sun.management.jmxremote.rmi.port=7199 " - + "-Djava.rmi.server.hostname=localhost " - + "-Dcom.sun.management.jmxremote=true " - + "-Dcom.sun.management.jmxremote.authenticate=false " - + "-Dcom.sun.management.jmxremote.ssl=false "); - container.setPortBindings(Arrays.asList("7199:7199", "4567:4567")); - return container; - } - - // Runs the JMX-monitored test container with SSL enabled - private static GenericContainer jmxSSLService(String jdkName) { - GenericContainer container = - getContainerFromDockerfile(jdkName) - .withExposedPorts(4567, 7199) - .withEnv( - "JAVA_OPTS", - "-Dcom.sun.management.jmxremote.port=7199 " - + "-Dcom.sun.management.jmxremote.rmi.port=7199 " - + "-Djava.rmi.server.hostname=localhost " - + "-Dcom.sun.management.jmxremote=true " - + "-Dcom.sun.management.jmxremote.authenticate=false " - + "-Dcom.sun.management.jmxremote.ssl=true " - + "-Dcom.sun.management.jmxremote.ssl.need.client.auth=true " - + "-Dcom.sun.management.jmxremote.registry.ssl=true " - + "-Djavax.net.ssl.keyStore=/serverkeystore " - + "-Djavax.net.ssl.keyStorePassword=serverpass " - + "-Djavax.net.ssl.trustStore=/servertruststore " - + "-Djavax.net.ssl.trustStorePassword=servertrustpass"); - container.setPortBindings(Arrays.asList("7199:7199", "4567:4567")); - return container; - } - - private static GenericContainer getContainerFromDockerfile(String jdkName) { - String sysProp = System.getProperty("TEST_SERVER_" + jdkName.toUpperCase(Locale.US)); - return new GenericContainer<>( - new ImageFromDockerfile().withFileFromFile(".", new File(sysProp))); - // .withFileFromFile( - // "bin", new File(System.getProperty("TEST_SERVER_DOCKER_FILES"), "bin")) - // .withFileFromFile( - // "lib", new File(System.getProperty("TEST_SERVER_DOCKER_FILES"), "lib"))); - } - - private static void eventually(long timeoutMs, Runnable r) throws Exception { - long timeoutNano = timeoutMs * 1_000_000; - long startTime = System.nanoTime(); - Exception lastException = null; - while (System.nanoTime() - timeoutNano < startTime) { - try { - r.run(); - return; - } catch (Exception e) { - lastException = e; - } - } - throw lastException; - } } diff --git a/test-nrjmx/src/test/resources/logback-test.xml b/test-nrjmx/src/test/resources/logback-test.xml index 5cdc1604..745e4f22 100644 --- a/test-nrjmx/src/test/resources/logback-test.xml +++ b/test-nrjmx/src/test/resources/logback-test.xml @@ -1,14 +1,14 @@ - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n - - + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + - - - + - - + + + + \ No newline at end of file diff --git a/test-server-jdk11/build.gradle.kts b/test-server-jdk11/build.gradle.kts index f9e21749..942883e6 100644 --- a/test-server-jdk11/build.gradle.kts +++ b/test-server-jdk11/build.gradle.kts @@ -8,9 +8,9 @@ repositories { } dependencies { - implementation("com.google.code.gson:gson:2.8.5") - implementation("com.sparkjava:spark-core:2.9.1") - implementation("org.slf4j:slf4j-simple:1.7.26") + implementation("com.google.code.gson:gson:2.8.6") + implementation("com.sparkjava:spark-core:2.9.2") + implementation("org.slf4j:slf4j-simple:1.7.30") } java { diff --git a/test-server-jdk8/build.gradle.kts b/test-server-jdk8/build.gradle.kts index fdebf06c..b160255c 100644 --- a/test-server-jdk8/build.gradle.kts +++ b/test-server-jdk8/build.gradle.kts @@ -8,9 +8,9 @@ repositories { } dependencies { - implementation ("com.google.code.gson:gson:2.8.5") - implementation ("com.sparkjava:spark-core:2.9.1") - implementation ("org.slf4j:slf4j-simple:1.7.26") + implementation("com.google.code.gson:gson:2.8.6") + implementation("com.sparkjava:spark-core:2.9.2") + implementation("org.slf4j:slf4j-simple:1.7.30") } java { diff --git a/test-server-jdk8/src/main/java/org/newrelic/jmx/Cat.java b/test-server-jdk8/src/main/java/org/newrelic/jmx/Cat.java index 46ad11f5..c7d8dce4 100644 --- a/test-server-jdk8/src/main/java/org/newrelic/jmx/Cat.java +++ b/test-server-jdk8/src/main/java/org/newrelic/jmx/Cat.java @@ -5,6 +5,7 @@ import javax.management.ObjectName; public class Cat implements CatMBean, MBeanRegistration { + private String name; public Cat(String name) { diff --git a/test-server-jdk8/src/main/java/org/newrelic/jmx/CatMBean.java b/test-server-jdk8/src/main/java/org/newrelic/jmx/CatMBean.java index 650319db..01f6e30d 100644 --- a/test-server-jdk8/src/main/java/org/newrelic/jmx/CatMBean.java +++ b/test-server-jdk8/src/main/java/org/newrelic/jmx/CatMBean.java @@ -1,5 +1,6 @@ package org.newrelic.jmx; public interface CatMBean { + String getName(); } diff --git a/test-server-jdk8/src/main/java/org/newrelic/jmx/Service.java b/test-server-jdk8/src/main/java/org/newrelic/jmx/Service.java index 09b69677..1c1f340e 100644 --- a/test-server-jdk8/src/main/java/org/newrelic/jmx/Service.java +++ b/test-server-jdk8/src/main/java/org/newrelic/jmx/Service.java @@ -11,6 +11,7 @@ import org.slf4j.LoggerFactory; public class Service { + public static void main(String[] args) throws Exception { final Logger log = LoggerFactory.getLogger("org.newrelic"); diff --git a/test-server-jdk8/src/main/resources/simplelogger.properties b/test-server-jdk8/src/main/resources/simplelogger.properties index 66957acb..5b4c4e28 100644 --- a/test-server-jdk8/src/main/resources/simplelogger.properties +++ b/test-server-jdk8/src/main/resources/simplelogger.properties @@ -1,9 +1,7 @@ org.slf4j.simpleLogger.defaultLogLevel=info org.slf4j.simpleLogger.logFile=System.out org.slf4j.simpleLogger.showDateTime=true - org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z - # Set to true if you want to output the current thread name. # Defaults to true. org.slf4j.simpleLogger.showThreadName=false