diff --git a/application/org.openjdk.jmc.feature.core/feature.xml b/application/org.openjdk.jmc.feature.core/feature.xml
index b1eff711d..5f332d436 100644
--- a/application/org.openjdk.jmc.feature.core/feature.xml
+++ b/application/org.openjdk.jmc.feature.core/feature.xml
@@ -196,4 +196,18 @@
version="0.0.0"
unpack="false"/>
+
+
+
+
diff --git a/application/org.openjdk.jmc.jolokia/.classpath b/application/org.openjdk.jmc.jolokia/.classpath
new file mode 100644
index 000000000..f15cbe421
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/.classpath
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/application/org.openjdk.jmc.jolokia/META-INF/MANIFEST.MF b/application/org.openjdk.jmc.jolokia/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..14dd1a5ef
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/META-INF/MANIFEST.MF
@@ -0,0 +1,20 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name
+Bundle-SymbolicName: org.openjdk.jmc.jolokia;singleton:=true
+Bundle-Version: 9.0.0.qualifier
+Automatic-Module-Name: org.openjdk.jmc.jolokia
+Bundle-RequiredExecutionEnvironment: JavaSE-17
+Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.ui,
+ org.jolokia.jmx-adapter.standalone;bundle-version="1.7.2";visibility:=reexport,
+ org.jolokia.jvm;bundle-version="1.7.2",
+ org.openjdk.jmc.common,
+ org.openjdk.jmc.rjmx,
+ org.openjdk.jmc.ui
+Export-Package: org.openjdk.jmc.jolokia,
+ org.openjdk.jmc.jolokia.preferences
+Import-Package: org.jolokia.converter,
+ org.json.simple
+Bundle-Activator: org.openjdk.jmc.jolokia.JmcJolokiaPlugin
+Bundle-ActivationPolicy: lazy
diff --git a/application/org.openjdk.jmc.jolokia/OSGI-INF/l10n/bundle.properties b/application/org.openjdk.jmc.jolokia/OSGI-INF/l10n/bundle.properties
new file mode 100644
index 000000000..11d361ea1
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/OSGI-INF/l10n/bundle.properties
@@ -0,0 +1,3 @@
+#Properties file for org.openjdk.jmc.jolokia
+page.name = Jolokia
+Bundle-Name = Jolokia JMC Connection
\ No newline at end of file
diff --git a/application/org.openjdk.jmc.jolokia/build.properties b/application/org.openjdk.jmc.jolokia/build.properties
new file mode 100644
index 000000000..49f8b881c
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/build.properties
@@ -0,0 +1,39 @@
+#
+# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2021, Kantega AS. All rights reserved.
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# The contents of this file are subject to the terms of either the Universal Permissive License
+# v 1.0 as shown at http://oss.oracle.com/licenses/upl
+#
+# or the following license:
+#
+# Redistribution and use in source and binary forms, with or without modification, are permitted
+# provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+# and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+# conditions and the following disclaimer in the documentation and/or other materials provided with
+# the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+# endorse or promote products derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+# WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+source.. = src/main/java/
+output.. = target/classes/
+bin.includes = META-INF/,\
+ OSGI-INF/,\
+ .,\
+ plugin.xml,\
+ OSGI-INF/l10n/bundle.properties
diff --git a/application/org.openjdk.jmc.jolokia/plugin.xml b/application/org.openjdk.jmc.jolokia/plugin.xml
new file mode 100644
index 000000000..a0a08b420
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/plugin.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/application/org.openjdk.jmc.jolokia/pom.xml b/application/org.openjdk.jmc.jolokia/pom.xml
new file mode 100644
index 000000000..2d4541b69
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/pom.xml
@@ -0,0 +1,49 @@
+
+
+
+ 4.0.0
+
+ org.openjdk.jmc
+ missioncontrol.application
+ ${revision}${changelist}
+
+ org.openjdk.jmc.jolokia
+ eclipse-plugin
+
+ ${project.basedir}/../../configuration
+
+
diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/AbstractCachedDescriptorProvider.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/AbstractCachedDescriptorProvider.java
new file mode 100644
index 000000000..b1eb561eb
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/AbstractCachedDescriptorProvider.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.jolokia;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.openjdk.jmc.rjmx.descriptorprovider.AbstractDescriptorProvider;
+import org.openjdk.jmc.rjmx.descriptorprovider.IDescriptorListener;
+
+/**
+ * The {@code AbstractCachedDescriptorProvider} keeps a list of identified JVMs that can be
+ * refreshed in the background by some means of discovering JVMs. Listeners will be notified of any
+ * changes.
+ */
+@SuppressWarnings("restriction")
+public abstract class AbstractCachedDescriptorProvider extends AbstractDescriptorProvider {
+
+ private static final long LOCAL_REFRESH_INTERVAL = 20000;
+ private Scanner scanner;
+ private Thread scannerThread;
+ /**
+ * Map
+ */
+ private final Map knownDescriptors = new HashMap<>();
+
+ /**
+ * This is where we periodically scan and report deltas to the listeners.
+ */
+ private class Scanner implements Runnable {
+ boolean isRunning;
+
+ @Override
+ public void run() {
+ isRunning = true;
+ while (isRunning) {
+ try {
+ scan();
+ Thread.sleep(LOCAL_REFRESH_INTERVAL);
+ } catch (InterruptedException ignore) {
+ // Don't mind being interrupted.
+ }
+ }
+ }
+
+ /**
+ * Marks this scanner as terminated.
+ */
+ public void shutdown() {
+ isRunning = false;
+ }
+
+ protected void scan() {
+ Map newOnes = discoverJvms();
+
+ synchronized (knownDescriptors) {
+ // Remove stale ones...
+ for (Iterator> entryIterator = knownDescriptors.entrySet()
+ .iterator(); entryIterator.hasNext();) {
+ Entry entry = entryIterator.next();
+ if (newOnes.containsKey(entry.getKey())) {
+ continue;
+ }
+ entryIterator.remove();
+ onDescriptorRemoved(entry.getKey());
+ }
+
+ // Add new ones...
+ for (Entry entry : newOnes.entrySet()) {
+ if (knownDescriptors.containsKey(entry.getKey())) {
+ continue;
+ }
+ onDescriptorDetected(entry.getValue(), entry.getValue().getPath(), entry.getValue().serviceUrl(),
+ entry.getValue());
+ }
+ knownDescriptors.clear();
+ knownDescriptors.putAll(newOnes);
+ }
+ }
+ }
+
+ /**
+ * Sets up the thread.
+ */
+ private void initialize() {
+
+ scanner = new Scanner();
+ scannerThread = new Thread(scanner, getName()); // $NON-NLS-1$
+ scannerThread.start();
+ }
+
+ protected abstract boolean isEnabled();
+
+ protected abstract Map discoverJvms();
+
+ @Override
+ public void addDescriptorListener(IDescriptorListener l) {
+ synchronized (m_descriptorListeners) {
+ if (m_descriptorListeners.size() == 0) {
+ super.addDescriptorListener(l);
+ initialize();
+ } else {
+ super.addDescriptorListener(l);
+ }
+ }
+ }
+
+ @Override
+ public void removeDescriptorListener(IDescriptorListener l) {
+ synchronized (m_descriptorListeners) {
+ super.removeDescriptorListener(l);
+ if (m_descriptorListeners.size() == 0 && scanner != null) {
+ scanner.shutdown();
+ scannerThread.interrupt();
+ }
+ }
+ }
+
+ /**
+ * Shuts down the scanner thread.
+ */
+ public void shutdown() {
+ if (scanner != null) {
+ scanner.shutdown();
+ }
+ }
+
+}
diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaJmxConnection.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaJmxConnection.java
new file mode 100644
index 000000000..bd8cdfec9
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaJmxConnection.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.jolokia;
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.util.LinkedList;
+import java.util.Optional;
+
+import javax.management.AttributeNotFoundException;
+import javax.management.Descriptor;
+import javax.management.ImmutableDescriptor;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.ObjectName;
+import javax.management.modelmbean.DescriptorSupport;
+import javax.management.openmbean.TabularData;
+
+import org.jolokia.client.J4pClient;
+import org.jolokia.client.jmxadapter.RemoteJmxAdapter;
+import org.jolokia.converter.Converters;
+import org.jolokia.converter.json.JsonConvertOptions;
+
+/**
+ * Make JMC specific adjustments to Jolokia JMX connection. May consider to use the decorator
+ * pattern if differences are big, but for now subclass
+ */
+public class JmcJolokiaJmxConnection extends RemoteJmxAdapter {
+
+ private static final String UNKNOWN = "Unknown"; //$NON-NLS-1$
+ private static final String DIAGNOSTIC_OPTIONS = "com.sun.management:type=DiagnosticCommand"; //$NON-NLS-1$
+ private static final String PREFIX = "dcmd."; //$NON-NLS-1$
+ private static final String IMPACT = PREFIX + "vmImpact"; //$NON-NLS-1$
+ private static final String NAME = PREFIX + "name"; //$NON-NLS-1$
+ private static final String DESCRIPTION = PREFIX + "description"; //$NON-NLS-1$
+ private static final String ARGUMENTS = PREFIX + "arguments"; //$NON-NLS-1$
+ private static final String ARGUMENT_NAME = PREFIX + "arg.name"; //$NON-NLS-1$
+ private static final String ARGUMENT_DESCRIPTION = PREFIX + "arg.description"; //$NON-NLS-1$
+ private static final String ARGUMENT_MANDATORY = PREFIX + "arg.isMandatory"; //$NON-NLS-1$
+ private static final String ARGUMENT_TYPE = PREFIX + "arg.type"; //$NON-NLS-1$
+ private static final String ARGUMENT_OPTION = PREFIX + "arg.isOption"; //$NON-NLS-1$
+ private static final String ARGUMENT_MULITPLE = PREFIX + "arg.isMultiple"; //$NON-NLS-1$
+
+ public JmcJolokiaJmxConnection(J4pClient client) throws IOException {
+ super(client);
+ }
+
+ @Override
+ public MBeanInfo getMBeanInfo(ObjectName name) throws InstanceNotFoundException, IOException {
+ MBeanInfo mBeanInfo = super.getMBeanInfo(name);
+ // the diagnostic options tab and memory relies on descriptor info in MBeanInfo,
+ // modify descriptors the first time
+ if (DIAGNOSTIC_OPTIONS.equals(name.getCanonicalName())
+ && mBeanInfo.getOperations()[0].getDescriptor() == ImmutableDescriptor.EMPTY_DESCRIPTOR) {
+
+ MBeanOperationInfo[] modifiedOperations = new MBeanOperationInfo[mBeanInfo.getOperations().length];
+
+ for (int i = 0; i < mBeanInfo.getOperations().length; i++) {
+ modifiedOperations[i] = stealOrBuildOperationInfo(mBeanInfo.getOperations()[i],
+ checkForLocalOperationInfo(name));
+ }
+ //create a copy with modified operations in place of the original MBeanInfo in the cache
+ final MBeanInfo modifiedMBeanInfo = new MBeanInfo(mBeanInfo.getClassName(), mBeanInfo.getDescription(),
+ mBeanInfo.getAttributes(), mBeanInfo.getConstructors(), modifiedOperations,
+ mBeanInfo.getNotifications());
+ this.mbeanInfoCache.put(name, modifiedMBeanInfo);
+ return modifiedMBeanInfo;
+ }
+ return mBeanInfo;
+ }
+
+ private Optional checkForLocalOperationInfo(ObjectName name) {
+ MBeanInfo localInfo;
+ try {
+ localInfo = ManagementFactory.getPlatformMBeanServer().getMBeanInfo(name);
+ } catch (Exception ignore) {
+ localInfo = null;
+ }
+ return Optional.ofNullable(localInfo);
+ }
+
+ @Override
+ public Object invoke(ObjectName name, String operationName, Object[] params, String[] signature)
+ throws InstanceNotFoundException, MBeanException, IOException {
+ for (int i = 0; i < params.length; i++) {
+ Object object = params[i];
+ if (object instanceof TabularData) {
+ try {
+ params[i] = new Converters().getToJsonConverter().convertToJson(object, new LinkedList(),
+ JsonConvertOptions.DEFAULT);
+ } catch (AttributeNotFoundException ignore) {
+ }
+ }
+
+ }
+ return super.invoke(name, operationName, params, signature);
+ }
+
+ /**
+ * Build MBeanOperationInfo by taking information from the corresponding MBean in the local JVM
+ * for a more precise signature. If it is not available locally, attempt to construct it from
+ * the metadata from Jolokia.
+ *
+ * @param original
+ * MBeanInfo from Jolokia list.
+ * @param localInfo
+ * MBeanInfo from this JVM to use for getting descriptor.
+ * @return Descriptor
+ */
+ private MBeanOperationInfo stealOrBuildOperationInfo(MBeanOperationInfo original, Optional localInfo) {
+ return localInfo.map(info -> checkForMatchingLocalOperation(original, info))// first attempt to get descriptor from local copy
+ .orElseGet(() -> reverseEngineerOperationInfo(original));// if not, reverse engineer descriptor from operation info
+ }
+
+ private MBeanOperationInfo checkForMatchingLocalOperation(MBeanOperationInfo original, MBeanInfo info) {
+ for (MBeanOperationInfo localOperation : info.getOperations()) {
+ if (localOperation.getName().equals(original.getName())) {
+ if (localOperation.getSignature().length == original.getSignature().length) {
+ for (int i = 0; i < original.getSignature().length; i++) {
+ MBeanParameterInfo param = original.getSignature()[i];
+ if (!param.getType().equals(localOperation.getSignature()[i].getType())) {
+ break;
+ } else if (i == original.getSignature().length - 1) {
+ // whole signature matches, use as replacement
+ return localOperation;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private MBeanOperationInfo reverseEngineerOperationInfo(MBeanOperationInfo original) {
+ DescriptorSupport result = new DescriptorSupport();
+ result.setField(NAME, original.getName());
+ result.setField(DESCRIPTION, original.getDescription());
+ result.setField(IMPACT, UNKNOWN);
+ result.setField(ARGUMENTS, buildArguments(original.getSignature()));
+ return new MBeanOperationInfo(original.getName(), original.getDescription(), original.getSignature(),
+ original.getReturnType(), MBeanOperationInfo.UNKNOWN, result);
+ }
+
+ private Descriptor buildArguments(MBeanParameterInfo[] signature) {
+ DescriptorSupport parameters = new DescriptorSupport();
+ for (MBeanParameterInfo parameter : signature) {
+ parameters.setField(parameter.getName(), buildArgument(parameter));
+ }
+ return parameters;
+ }
+
+ private Descriptor buildArgument(MBeanParameterInfo parameter) {
+ DescriptorSupport result = new DescriptorSupport();
+ result.setField(ARGUMENT_NAME, parameter.getName());
+ boolean isMultiple = parameter.getType().startsWith("["); //$NON-NLS-1$
+ result.setField(ARGUMENT_MULITPLE, String.valueOf(isMultiple));
+ String type = parameter.getType();
+ if (isMultiple) {
+ if (type.startsWith("[L")) { //$NON-NLS-1$
+ type = type.substring(2);
+ } else {
+ type = type.substring(1);
+ }
+
+ }
+ // probably more reverse mapping of types should be done here, but we hope it is
+ // sufficient
+ result.setField(ARGUMENT_TYPE, type);
+ result.setField(ARGUMENT_DESCRIPTION, parameter.getDescription());
+ result.setField(ARGUMENT_MANDATORY, "false"); //$NON-NLS-1$
+ result.setField(ARGUMENT_OPTION, "false"); //$NON-NLS-1$
+ return result;
+ }
+
+}
diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaJmxConnectionProvider.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaJmxConnectionProvider.java
new file mode 100644
index 000000000..d1f0ca841
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaJmxConnectionProvider.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.jolokia;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.Map;
+
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorProvider;
+import javax.management.remote.JMXServiceURL;
+
+public class JmcJolokiaJmxConnectionProvider implements JMXConnectorProvider {
+ @Override
+ public JMXConnector newJMXConnector(JMXServiceURL serviceURL, Map environment) throws IOException {
+ if (!"jolokia".equals(serviceURL.getProtocol())) { //$NON-NLS-1$
+ throw new MalformedURLException("I only serve Jolokia connections"); //$NON-NLS-1$
+ }
+ return new JmcJolokiaJmxConnector(serviceURL, environment);
+ }
+}
diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaJmxConnector.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaJmxConnector.java
new file mode 100644
index 000000000..2a0835ed1
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaJmxConnector.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.jolokia;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.management.remote.JMXServiceURL;
+
+import org.jolokia.client.J4pClientBuilder;
+import org.jolokia.client.jmxadapter.JolokiaJmxConnector;
+import org.jolokia.client.jmxadapter.RemoteJmxAdapter;
+
+public class JmcJolokiaJmxConnector extends JolokiaJmxConnector {
+
+ public JmcJolokiaJmxConnector(JMXServiceURL serviceURL, Map environment) {
+ super(serviceURL, environment);
+ }
+
+ @Override
+ protected RemoteJmxAdapter instantiateAdapter(J4pClientBuilder clientBuilder, Map mergedEnv)
+ throws IOException {
+ return new JmcJolokiaJmxConnection(clientBuilder.build());
+ }
+}
diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaPlugin.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaPlugin.java
new file mode 100644
index 000000000..94816d42f
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaPlugin.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.jolokia;
+
+import org.openjdk.jmc.ui.MCAbstractUIPlugin;
+
+public class JmcJolokiaPlugin extends MCAbstractUIPlugin {
+
+ public final static String PLUGIN_ID = "org.openjdk.jmc.jolokia"; //$NON-NLS-1$
+ private static JmcJolokiaPlugin plugin;
+
+ public JmcJolokiaPlugin() {
+ super(PLUGIN_ID);
+ plugin = this;
+ }
+
+ public static JmcJolokiaPlugin getDefault() {
+ return plugin;
+ }
+}
diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JolokiaAgentDescriptor.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JolokiaAgentDescriptor.java
new file mode 100644
index 000000000..b872c28c4
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JolokiaAgentDescriptor.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.jolokia;
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Map;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.InstanceNotFoundException;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.remote.JMXServiceURL;
+
+import org.jolokia.client.jmxadapter.RemoteJmxAdapter;
+import org.openjdk.jmc.common.jvm.Connectable;
+import org.openjdk.jmc.common.jvm.JVMArch;
+import org.openjdk.jmc.common.jvm.JVMDescriptor;
+import org.openjdk.jmc.common.jvm.JVMType;
+
+/**
+ * Provide data about JVMs accessed over Jolokia for the JVM browser
+ */
+public class JolokiaAgentDescriptor implements ServerConnectionDescriptor {
+
+ public static final JVMDescriptor NULL_DESCRIPTOR = new JVMDescriptor(null, null, null, null, null, null, null,
+ null, false, Connectable.UNKNOWN);
+ private final JMXServiceURL serviceUrl;
+ private final Map agentData;
+ private final JVMDescriptor jvmDescriptor;
+
+ public JolokiaAgentDescriptor(Map agentData, JVMDescriptor jvmDescriptor)
+ throws URISyntaxException, MalformedURLException {
+ super();
+ URI uri = new URI((String) agentData.get("url")); //$NON-NLS-1$
+ this.serviceUrl = new JMXServiceURL(
+ String.format("service:jmx:jolokia://%s:%s%s", uri.getHost(), uri.getPort(), uri.getPath())); //$NON-NLS-1$
+ this.agentData = agentData;
+ this.jvmDescriptor = jvmDescriptor;
+ }
+
+ JMXServiceURL getServiceUrl() {
+ return serviceUrl;
+ }
+
+ @Override
+ public String getGUID() {
+ return String.valueOf(agentData.get("agent_id")); //$NON-NLS-1$
+ }
+
+ @Override
+ public String getDisplayName() {
+ return String.valueOf(agentData.get("agent_id")); //$NON-NLS-1$
+ }
+
+ @Override
+ public JVMDescriptor getJvmInfo() {
+ return this.jvmDescriptor;
+ }
+
+ /**
+ * Best effort to extract JVM information from a connection if everything works. Can be adjusted
+ * to support different flavors of JVM.
+ */
+ public static JVMDescriptor attemptToGetJvmInfo(RemoteJmxAdapter adapter) {
+
+ try {
+ AttributeList attributes = adapter.getAttributes(new ObjectName(ManagementFactory.RUNTIME_MXBEAN_NAME),
+ new String[] {"Pid", "Name", "InputArguments", "SystemProperties"}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ Integer pid = null;
+ String arguments = null, javaCommand = null, javaVersion = null, vmName = null, vmVendor = null;
+ boolean isDebug = false;
+ JVMType type = JVMType.UNKNOWN;
+ JVMArch arch = JVMArch.UNKNOWN;
+ for (Attribute attribute : attributes.asList()) {
+ // newer JVM have pid as separate attribute, older have to parse from name
+ if (attribute.getName().equalsIgnoreCase("Pid")) { //$NON-NLS-1$
+ try {
+ pid = Integer.valueOf(String.valueOf(attribute.getValue()));
+ } catch (NumberFormatException ignore) {
+ }
+ } else if (attribute.getName().equalsIgnoreCase("Name") && pid == null) { //$NON-NLS-1$
+ String pidAndHost = String.valueOf(attribute.getValue());
+ int separator = pidAndHost.indexOf('@');
+ if (separator > 0) {
+ try {
+ pid = Integer.valueOf(pidAndHost.substring(0, separator));
+ } catch (NumberFormatException e) {
+ }
+ }
+ } else if (attribute.getName().equalsIgnoreCase("InputArguments")) { //$NON-NLS-1$
+
+ if (attribute.getValue() instanceof String[]) {
+ arguments = Arrays.toString((String[]) attribute.getValue());
+ } else {
+ arguments = String.valueOf(attribute.getValue());
+ }
+ if (arguments.contains("-agentlib:jdwp")) { //$NON-NLS-1$
+ isDebug = true;
+ }
+ } else if (attribute.getName().equalsIgnoreCase("SystemProperties") //$NON-NLS-1$
+ && attribute.getValue() instanceof TabularDataSupport) {
+ TabularDataSupport systemProperties = (TabularDataSupport) attribute.getValue();
+
+ // quite clumsy: iterate over properties as we need to use the exact key, which is non trivial
+ // to reproduce
+ for (Object entry : systemProperties.values()) {
+ String key = ((CompositeDataSupport) entry).get("key").toString(); //$NON-NLS-1$
+ String value = ((CompositeDataSupport) entry).get("value").toString(); //$NON-NLS-1$
+ if (key.equalsIgnoreCase("sun.management.compiler")) { //$NON-NLS-1$
+ if (value.toLowerCase().contains("hotspot")) { //$NON-NLS-1$
+ type = JVMType.HOTSPOT;
+ }
+ } else if (key.equalsIgnoreCase("sun.arch.data.model")) { //$NON-NLS-1$
+ String archIndicator = value;
+ if (archIndicator.contains("64")) { //$NON-NLS-1$
+ arch = JVMArch.BIT64;
+ } else if (archIndicator.contains("32")) { //$NON-NLS-1$
+ arch = JVMArch.BIT32;
+ }
+ } else if (key.equalsIgnoreCase("sun.java.command")) { //$NON-NLS-1$
+ javaCommand = value;
+ } else if (key.equalsIgnoreCase("java.version")) { //$NON-NLS-1$
+ javaVersion = value;
+ } else if (key.equalsIgnoreCase("java.vm.name")) { //$NON-NLS-1$
+ vmName = value;
+ } else if (key.equalsIgnoreCase("java.vm.vendor")) { //$NON-NLS-1$
+ vmVendor = value;
+ }
+ }
+
+ }
+
+ }
+ return new JVMDescriptor(javaVersion, type, arch, javaCommand, arguments, vmName, vmVendor, pid, isDebug,
+ Connectable.UNKNOWN);
+
+ } catch (RuntimeException | IOException | InstanceNotFoundException | MalformedObjectNameException ignore) {
+ return NULL_DESCRIPTOR;
+ }
+
+ }
+
+ @Override
+ public JMXServiceURL createJMXServiceURL() throws IOException {
+ return serviceUrl;
+ }
+
+ @Override
+ public Map getEnvironment() {
+ return null;
+ }
+
+ @Override
+ public String getPath() {
+ return null;
+ }
+
+ @Override
+ public JMXServiceURL serviceUrl() {
+ return this.serviceUrl;
+ }
+
+}
diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JolokiaDiscoveryListener.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JolokiaDiscoveryListener.java
new file mode 100644
index 000000000..dd5b36458
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JolokiaDiscoveryListener.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.jolokia;
+
+import java.io.IOException;
+
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jolokia.client.jmxadapter.RemoteJmxAdapter;
+import org.jolokia.discovery.JolokiaDiscovery;
+import org.jolokia.util.JulLogHandler;
+import org.openjdk.jmc.common.jvm.JVMDescriptor;
+import org.openjdk.jmc.jolokia.preferences.PreferenceConstants;
+
+/**
+ * Add found Jolokia instances to the JVM browser using the Jolokia discovery mechanism.
+ * https://jolokia.org/reference/html/protocol.html#discovery
+ */
+public class JolokiaDiscoveryListener extends AbstractCachedDescriptorProvider implements PreferenceConstants {
+
+ @Override
+ protected Map discoverJvms() {
+ Map found = new HashMap<>();
+ if (!JmcJolokiaPlugin.getDefault().getPreferenceStore().getBoolean(P_SCAN)) {
+ return found;
+ }
+ try {
+ for (Object object : new JolokiaDiscovery("jmc", new JulLogHandler()).lookupAgents()) { //$NON-NLS-1$
+ try {
+
+ @SuppressWarnings("unchecked")
+ Map response = (Map) object;
+ JVMDescriptor jvmInfo;
+ try {// if it is connectable, see if we can get info from connection
+ jvmInfo = JolokiaAgentDescriptor
+ .attemptToGetJvmInfo(new RemoteJmxAdapter(String.valueOf(response.get("url")))); //$NON-NLS-1$
+ } catch (Exception ignore) {
+ jvmInfo = JolokiaAgentDescriptor.NULL_DESCRIPTOR;
+ }
+ JolokiaAgentDescriptor agentDescriptor = new JolokiaAgentDescriptor(response, jvmInfo);
+ found.put(agentDescriptor.getGUID(), agentDescriptor);
+
+ } catch (URISyntaxException ignore) {
+ }
+ }
+ } catch (IOException ignore) {
+ }
+ return found;
+ }
+
+ @Override
+ public String getDescription() {
+ return Messages.JolokiaDiscoveryListener_Description;
+ }
+
+ @Override
+ public String getName() {
+ return "jolokia"; //$NON-NLS-1$
+ }
+
+ @Override
+ protected boolean isEnabled() {
+ return true;
+ }
+
+}
diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/Messages.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/Messages.java
new file mode 100644
index 000000000..7f9321d39
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/Messages.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.jolokia;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.openjdk.jmc.jolokia.messages"; //$NON-NLS-1$
+ public static String JolokiaDiscoveryListener_Description;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/ServerConnectionDescriptor.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/ServerConnectionDescriptor.java
new file mode 100644
index 000000000..01e9fd1bf
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/ServerConnectionDescriptor.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.jolokia;
+
+import javax.management.remote.JMXServiceURL;
+
+import org.openjdk.jmc.rjmx.IConnectionDescriptor;
+import org.openjdk.jmc.rjmx.IServerDescriptor;
+
+/**
+ * Describes the JVM and how to connect to it.
+ */
+public interface ServerConnectionDescriptor extends IServerDescriptor, IConnectionDescriptor {
+ String getPath();
+
+ JMXServiceURL serviceUrl();
+}
diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/messages.properties b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/messages.properties
new file mode 100644
index 000000000..37881ad5b
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/messages.properties
@@ -0,0 +1 @@
+JolokiaDiscoveryListener_Description=Uses Jolokia Discovery to report any active JVMs with Jolokia broadcasting
diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/JolokiaPreferencePage.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/JolokiaPreferencePage.java
new file mode 100644
index 000000000..1f606eaf3
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/JolokiaPreferencePage.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.jolokia.preferences;
+
+import org.eclipse.jface.preference.BooleanFieldEditor;
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.openjdk.jmc.jolokia.JmcJolokiaPlugin;
+
+public class JolokiaPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {
+
+ public JolokiaPreferencePage() {
+ super(GRID);
+ setPreferenceStore(JmcJolokiaPlugin.getDefault().getPreferenceStore());
+ setDescription(Messages.JolokiaPreferencePage_Description);
+ }
+
+ public void createFieldEditors() {
+ addField(new BooleanFieldEditor(PreferenceConstants.P_SCAN, Messages.JolokiaPreferencePage_Label,
+ getFieldEditorParent()));
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
+ */
+ public void init(IWorkbench workbench) {
+ }
+
+}
diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/Messages.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/Messages.java
new file mode 100644
index 000000000..bb526be23
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/Messages.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.jolokia.preferences;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.openjdk.jmc.jolokia.preferences.messages"; //$NON-NLS-1$
+ public static String JolokiaPreferencePage_Description;
+ public static String JolokiaPreferencePage_Label;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/PreferenceConstants.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/PreferenceConstants.java
new file mode 100644
index 000000000..1d14211b6
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/PreferenceConstants.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.jolokia.preferences;
+
+/**
+ * Constant definitions for plug-in preferences.
+ */
+public interface PreferenceConstants {
+
+ public static final String P_SCAN = "discoverJolokia"; //$NON-NLS-1$
+
+}
diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/PreferenceInitializer.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/PreferenceInitializer.java
new file mode 100644
index 000000000..f3c41d45a
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/PreferenceInitializer.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.jolokia.preferences;
+
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.openjdk.jmc.jolokia.JmcJolokiaPlugin;
+
+/**
+ * Class used to initialize default preference values.
+ */
+public class PreferenceInitializer extends AbstractPreferenceInitializer {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer#
+ * initializeDefaultPreferences()
+ */
+ public void initializeDefaultPreferences() {
+ IPreferenceStore store = JmcJolokiaPlugin.getDefault().getPreferenceStore();
+ store.setDefault(PreferenceConstants.P_SCAN, false);
+ }
+
+}
diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/messages.properties b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/messages.properties
new file mode 100644
index 000000000..d81f04ddf
--- /dev/null
+++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/messages.properties
@@ -0,0 +1,2 @@
+JolokiaPreferencePage_Description=Discover Jolokia Agents\n\nhttps://jolokia.org/reference/html/protocol.html\#discovery\n\n
+JolokiaPreferencePage_Label=&Discover Jolokia agents
diff --git a/application/org.openjdk.jmc.kubernetes/.classpath b/application/org.openjdk.jmc.kubernetes/.classpath
new file mode 100644
index 000000000..29127e464
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/.classpath
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/application/org.openjdk.jmc.kubernetes/.gitignore b/application/org.openjdk.jmc.kubernetes/.gitignore
new file mode 100644
index 000000000..ae3c17260
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/.gitignore
@@ -0,0 +1 @@
+/bin/
diff --git a/application/org.openjdk.jmc.kubernetes/META-INF/MANIFEST.MF b/application/org.openjdk.jmc.kubernetes/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..68a0132f9
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/META-INF/MANIFEST.MF
@@ -0,0 +1,19 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name
+Bundle-SymbolicName: org.openjdk.jmc.kubernetes;singleton:=true
+Bundle-Version: 9.0.0.qualifier
+Automatic-Module-Name: org.openjdk.jmc.kubernetes
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Require-Bundle: org.openjdk.jmc.jolokia,
+ org.eclipse.core.runtime,
+ org.eclipse.ui,
+ org.openjdk.jmc.ui,
+ org.eclipse.swt,
+ org.jolokia.kubernetes.standalone;bundle-version="1.7.2",
+ org.openjdk.jmc.rjmx
+Export-Package: org.openjdk.jmc.kubernetes,
+ org.openjdk.jmc.kubernetes.preferences
+Import-Package: org.apache.commons.logging;version="1.2.0"
+Bundle-Activator: org.openjdk.jmc.kubernetes.JmcKubernetesPlugin
+Bundle-ActivationPolicy: lazy
diff --git a/application/org.openjdk.jmc.kubernetes/OSGI-INF/l10n/bundle.properties b/application/org.openjdk.jmc.kubernetes/OSGI-INF/l10n/bundle.properties
new file mode 100644
index 000000000..83ca6439d
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/OSGI-INF/l10n/bundle.properties
@@ -0,0 +1,3 @@
+#Properties file for org.openjdk.jmc.kubernetes
+page.name = Kubernetes
+Bundle-Name = Kubernetes JMC extensions
\ No newline at end of file
diff --git a/application/org.openjdk.jmc.kubernetes/build.properties b/application/org.openjdk.jmc.kubernetes/build.properties
new file mode 100644
index 000000000..25bd92d04
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/build.properties
@@ -0,0 +1,7 @@
+source.. = src/main/java/
+output.. = target/classes/
+bin.includes = META-INF/,\
+ OSGI-INF/,\
+ .,\
+ OSGI-INF/l10n/bundle.properties,\
+ plugin.xml
diff --git a/application/org.openjdk.jmc.kubernetes/plugin.xml b/application/org.openjdk.jmc.kubernetes/plugin.xml
new file mode 100644
index 000000000..3f2eaf104
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/plugin.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/application/org.openjdk.jmc.kubernetes/pom.xml b/application/org.openjdk.jmc.kubernetes/pom.xml
new file mode 100644
index 000000000..2d10e6cd9
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/pom.xml
@@ -0,0 +1,49 @@
+
+
+
+ 4.0.0
+
+ org.openjdk.jmc
+ missioncontrol.application
+ ${revision}${changelist}
+
+ org.openjdk.jmc.kubernetes
+ eclipse-plugin
+
+ ${project.basedir}/../../configuration
+
+
diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java
new file mode 100644
index 000000000..4c9a58904
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.kubernetes;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.regex.Pattern;
+
+import javax.management.InstanceNotFoundException;
+
+import org.jolokia.client.J4pClient;
+import org.jolokia.client.exception.J4pException;
+import org.jolokia.client.exception.J4pRemoteException;
+import org.jolokia.client.request.J4pResponse;
+import org.openjdk.jmc.jolokia.JmcJolokiaJmxConnection;
+import org.openjdk.jmc.rjmx.ConnectionException;
+
+/**
+ * Jolokia based MBeanServerConnector tailored for JMC needs
+ */
+public class JmcKubernetesJmxConnection extends JmcJolokiaJmxConnection {
+
+ static final Collection DISCONNECT_SIGNS = Arrays.asList(Pattern.compile("Error: pods \".+\" not found")); //$NON-NLS-1$
+
+ public JmcKubernetesJmxConnection(J4pClient client) throws IOException {
+ super(client);
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ protected J4pResponse unwrapException(J4pException e) throws IOException, InstanceNotFoundException {
+ // recognize signs of disconnect and signal to the application for better
+ // handling
+ if (isKnownDisconnectException(e)) {
+ throw new ConnectionException(e.getMessage());
+ } else {
+ return super.unwrapException(e);
+ }
+ }
+
+ private boolean isKnownDisconnectException(J4pException e) {
+ if (!(e instanceof J4pRemoteException)) {
+ return false;
+ }
+ if (!"io.fabric8.kubernetes.client.KubernetesClientException".equals(((J4pRemoteException) e).getErrorType())) { //$NON-NLS-1$
+ return false;
+ }
+ return DISCONNECT_SIGNS.stream().anyMatch(pattern -> pattern.matcher(e.getMessage()).matches());
+ }
+
+}
diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java
new file mode 100644
index 000000000..2ce8c4392
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.kubernetes;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.Map;
+
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorProvider;
+import javax.management.remote.JMXServiceURL;
+
+/**
+ * This {@code JMXConnectionProvider} handles the "kubernetes" protocol.
+ */
+public class JmcKubernetesJmxConnectionProvider implements JMXConnectorProvider {
+ @Override
+ public JMXConnector newJMXConnector(JMXServiceURL serviceURL, Map environment) throws IOException {
+ if (!"kubernetes".equals(serviceURL.getProtocol())) { //$NON-NLS-1$
+ throw new MalformedURLException("I only serve Kubernetes connections"); //$NON-NLS-1$
+ }
+ return new JmcKubernetesJmxConnector(serviceURL, environment);
+ }
+}
diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java
new file mode 100644
index 000000000..b15f1e462
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.kubernetes;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.management.remote.JMXServiceURL;
+
+import org.jolokia.client.J4pClient;
+import org.jolokia.client.jmxadapter.RemoteJmxAdapter;
+import org.jolokia.kubernetes.client.KubernetesJmxConnector;
+
+public class JmcKubernetesJmxConnector extends KubernetesJmxConnector {
+
+ public JmcKubernetesJmxConnector(JMXServiceURL serviceURL, Map environment) {
+ super(serviceURL, environment);
+ }
+
+ @Override
+ protected RemoteJmxAdapter createAdapter(J4pClient client) throws IOException {
+ return new JmcKubernetesJmxConnection(client);
+ }
+}
diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java
new file mode 100644
index 000000000..bef09cbf7
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.kubernetes;
+
+import org.eclipse.core.runtime.Platform;
+import org.openjdk.jmc.kubernetes.preferences.KubernetesScanningParameters;
+import org.openjdk.jmc.kubernetes.preferences.PreferenceConstants;
+import org.openjdk.jmc.ui.MCAbstractUIPlugin;
+import org.openjdk.jmc.common.security.ICredentials;
+import org.openjdk.jmc.common.security.PersistentCredentials;
+import org.openjdk.jmc.common.security.SecurityException;
+import org.openjdk.jmc.common.security.SecurityManagerFactory;
+import org.openjdk.jmc.ui.misc.DisplayToolkit;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+
+public class JmcKubernetesPlugin extends MCAbstractUIPlugin
+ implements KubernetesScanningParameters, PreferenceConstants {
+
+ public final static String PLUGIN_ID = "org.openjdk.jmc.kubernetes"; //$NON-NLS-1$
+
+ // The shared instance.
+ private static JmcKubernetesPlugin plugin;
+
+ /**
+ * The constructor.
+ */
+ public JmcKubernetesPlugin() {
+ super(PLUGIN_ID);
+ plugin = this;
+ }
+
+ /**
+ * @return the shared instance.
+ */
+ public static JmcKubernetesPlugin getDefault() {
+ return plugin;
+ }
+
+ private void ensureNeededCredentialsAreUnlocked() {
+ if (getScanningCredentials() != null && SecurityManagerFactory.getSecurityManager().isLocked()) {
+ DisplayToolkit.safeAsyncExec(() -> {
+ try {
+ SecurityManagerFactory.getSecurityManager().unlock();
+ } catch (SecurityException e) {
+ logError("Error unlocking credentials needed for kubernetes scanning", e);//$NON-NLS-1$
+ }
+ });
+ }
+ }
+
+ @Override
+ public boolean scanForInstances() {
+ // If credentials are locked and credentials are required, the scanner thread
+ // will get hung
+ // therefore await credentials store to be unlocked before proceeding to scan
+ return getPreferenceStore().getBoolean(P_SCAN_FOR_INSTANCES)
+ && (getScanningCredentials() == null || !SecurityManagerFactory.getSecurityManager().isLocked());
+
+ }
+
+ @Override
+ public boolean scanAllContexts() {
+ return getPreferenceStore().getBoolean(P_SCAN_ALL_CONTEXTS);
+ }
+
+ @Override
+ public String jolokiaPort() {
+ return getPreferenceStore().getString(P_JOLOKIA_PORT);
+ }
+
+ private PersistentCredentials getScanningCredentials() {
+ String key = getPreferenceStore().getString(P_CREDENTIALS_KEY);
+ return key == null ? null : new PersistentCredentials(key);
+ }
+
+ public ICredentials storeCredentials(String username, String password) throws SecurityException {
+ PersistentCredentials credentials = new PersistentCredentials(username, password, "kubernetes");//$NON-NLS-1$
+ getPreferenceStore().setValue(P_CREDENTIALS_KEY, credentials.getExportedId());
+ return credentials;
+ }
+
+ @Override
+ public String username() throws SecurityException {
+ final PersistentCredentials cred = getScanningCredentials();
+ if (cred == null) {
+ return "";//$NON-NLS-1$
+ } else {
+ return cred.getUsername();
+ }
+ }
+
+ @Override
+ public String password() throws SecurityException {
+ final PersistentCredentials cred = getScanningCredentials();
+ if (cred == null) {
+ return "";//$NON-NLS-1$
+ } else {
+ return cred.getPassword();
+ }
+ }
+
+ @Override
+ public String jolokiaPath() {
+ return getPreferenceStore().getString(P_JOLOKIA_PATH);
+ }
+
+ @Override
+ public String requireLabel() {
+ return getPreferenceStore().getString(P_REQUIRE_LABEL);
+ }
+
+ @Override
+ public String jolokiaProtocol() {
+ return getPreferenceStore().getString(P_JOLOKIA_PROTOCOL);
+ }
+
+ @Override
+ public void logError(String message, Throwable error) {
+ Platform.getLog(FrameworkUtil.getBundle(getClass())).error(message, error);
+ }
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ this.ensureNeededCredentialsAreUnlocked();
+ }
+
+}
diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java
new file mode 100644
index 000000000..92dedd036
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.kubernetes;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.Properties;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXServiceURL;
+
+import org.apache.commons.codec.binary.Base64;
+import org.jolokia.client.J4pClient;
+import org.jolokia.kubernetes.client.KubernetesJmxConnector;
+import org.jolokia.util.AuthorizationHeaderParser;
+import org.jolokia.util.Base64Util;
+import org.openjdk.jmc.jolokia.AbstractCachedDescriptorProvider;
+import org.openjdk.jmc.jolokia.JolokiaAgentDescriptor;
+import org.openjdk.jmc.jolokia.ServerConnectionDescriptor;
+import org.openjdk.jmc.kubernetes.preferences.KubernetesScanningParameters;
+import org.openjdk.jmc.common.jvm.JVMDescriptor;
+import org.openjdk.jmc.common.security.SecurityException;
+
+import io.fabric8.kubernetes.api.model.NamedContext;
+import io.fabric8.kubernetes.api.model.ObjectMeta;
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.api.model.PodList;
+import io.fabric8.kubernetes.api.model.Secret;
+import io.fabric8.kubernetes.client.Config;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.Watch;
+import io.fabric8.kubernetes.client.Watcher;
+import io.fabric8.kubernetes.client.dsl.FilterWatchListMultiDeletable;
+import io.fabric8.kubernetes.client.internal.KubeConfigUtils;
+import io.fabric8.kubernetes.client.utils.Utils;
+
+/**
+ * This class attempts to connect to JVMs in pods running in kubernetes in a background thread.
+ * Enablement and parameters for the scanning is given by user preferences.
+ */
+public class KubernetesDiscoveryListener extends AbstractCachedDescriptorProvider {
+
+ private final static Pattern SECRET_PATTERN = Pattern
+ .compile("\\$\\{kubernetes/secret/(?[^/]+)/(?[^\\}]+)}"); //$NON-NLS-1$
+ private final static Pattern ATTRIBUTE_PATTERN = Pattern
+ .compile("\\$\\{kubernetes/annotation/(?[^/]+)}"); //$NON-NLS-1$
+ private final static Set VALID_JOLOKIA_PROTOCOLS = new HashSet<>(Arrays.asList("http", "https")); //$NON-NLS-1$ //$NON-NLS-2$
+
+ KubernetesScanningParameters settings;
+
+ public KubernetesDiscoveryListener() {
+ this(JmcKubernetesPlugin.getDefault());
+ }
+
+ //Public constructor in order for test plugin to be able to rig tests in an easier manner
+ public KubernetesDiscoveryListener(KubernetesScanningParameters parameters) {
+ this.settings = parameters;
+ }
+
+ public final String getDescription() {
+ return Messages.KubernetesDiscoveryListener_Description;
+ }
+
+ @Override
+ public String getName() {
+ return "kubernetes"; //$NON-NLS-1$
+ }
+
+ boolean notEmpty(String value) {
+ return value != null && value.length() > 0;
+ }
+
+ private List contexts;
+ private long contextsCached = 0L;
+
+ private List allContexts() throws IOException {
+ final String path = Utils.getSystemPropertyOrEnvVar(Config.KUBERNETES_KUBECONFIG_FILE,
+ new File(System.getProperty("user.home"), ".kube" + File.separator + "config").toString()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ File configPath = new File(path);
+ if (contexts != null && contextsCached > configPath.lastModified()) {// the YAML parsing is soo incredibly
+ // sloow, hence cache context names for
+ // later
+ // runs
+ return contexts;
+ }
+ // reload config if kubeconfig has been modified since we cached the config
+ io.fabric8.kubernetes.api.model.Config config = KubeConfigUtils.parseConfig(configPath);
+ this.contextsCached = System.currentTimeMillis();
+ KubernetesJmxConnector.resetKubernetesConfig();
+ return contexts = config.getContexts().stream().map(NamedContext::getName).collect(Collectors.toList());
+ }
+
+ @Override
+ protected Map discoverJvms() {
+ return scanForJvms();
+ }
+
+ Map scanForJvms() {
+ Map found = new HashMap<>();
+ if (!isEnabled()) {
+ return found;
+ }
+ boolean hasScanned = false;
+
+ if (settings.scanAllContexts()) {
+ try {
+ for (final String context : allContexts()) {
+ hasScanned = true;
+ scanContext(found, settings, context);
+ }
+ } catch (IOException e) {
+ settings.logError(Messages.KubernetesDiscoveryListener_UnableToFindContexts, e);
+ }
+ }
+ if (!hasScanned) {// scan default context
+ return scanContext(found, settings, null);
+ }
+ return found;
+ }
+
+ private Map scanContext(
+ Map found, KubernetesScanningParameters parameters, String context) {
+ try {
+ scanContextUnsafe(found, parameters, context);
+ } catch (Exception e) {
+ parameters.logError(Messages.KubernetesDiscoveryListener_UnableToScan + context, e);
+ }
+ return found;
+ }
+
+ private Map scanContextUnsafe(
+ Map found, KubernetesScanningParameters parameters, String context) {
+ String pathLabel = parameters.requireLabel();
+ KubernetesClient client = KubernetesJmxConnector.getApiClient(context);
+
+ FilterWatchListMultiDeletable> query = client.pods()
+ .inAnyNamespace();
+ List podList;
+ boolean hasPathLabel = notEmpty(pathLabel);
+ if (hasPathLabel) {
+ podList = query.withLabel(pathLabel).list().getItems();
+ } else {
+ podList = query.list().getItems();
+ }
+ // can consider parallelization for big contexts, however since it is the
+ // background await the situation a bit
+ podList.stream().forEach(pod -> scanPod(found, parameters, context, client, pod));
+ return found;
+ }
+
+ private void scanPod(
+ Map found, KubernetesScanningParameters parameters, String context,
+ KubernetesClient client, Pod pod) {
+
+ final ObjectMeta metadata = pod.getMetadata();
+ HashMap headers = new HashMap<>();
+ Map env = new HashMap<>();
+ try {
+ if (notEmpty(parameters.username())) {
+ if (!notEmpty(parameters.password())) {
+ throw new IllegalArgumentException(Messages.KubernetesDiscoveryListener_MustProvidePassword);
+ }
+ authorize(headers, client, parameters.username(), parameters.password(), metadata.getNamespace(), env);
+ }
+ } catch (SecurityException e) {
+ // skipping authorization if anything fails
+ }
+ final StringBuilder url = new StringBuilder("/api/").append(pod.getApiVersion()).append("/namespaces/")
+ .append(metadata.getNamespace()).append("/pods/");
+ // JMX url must be reverse constructed, so that we can connect from the
+ // resulting node in the JVM browser
+ final StringBuilder jmxUrl = new StringBuilder("service:jmx:kubernetes:///").append(metadata.getNamespace()) //$NON-NLS-1$
+ .append('/');
+
+ final String protocol = getValueOrAttribute(parameters.jolokiaProtocol(), metadata);
+ final String podName = metadata.getName();
+ if (notEmpty(protocol)) {
+ if (!VALID_JOLOKIA_PROTOCOLS.contains(protocol)) {
+ throw new IllegalArgumentException(Messages.KubernetesDiscoveryListener_JolokiaProtocol + protocol
+ + Messages.KubernetesDiscoveryListener_HttpOrHttps);
+ }
+ // a bit clumsy, need to inject protocol _before_ podname in selflink
+ url.append(protocol).append(':');
+ jmxUrl.append(protocol).append(':');
+ }
+
+ jmxUrl.append(podName);
+ url.append(podName);
+
+ final String port = getValueOrAttribute(parameters.jolokiaPort(), metadata);
+ if (port != null) {
+ url.append(":").append(port); //$NON-NLS-1$
+ jmxUrl.append(':').append(port);
+ }
+
+ url.append("/proxy"); //$NON-NLS-1$
+
+ final String path = getValueOrAttribute(parameters.jolokiaPath(), metadata);
+
+ if (!path.startsWith("/")) { //$NON-NLS-1$
+ url.append('/');
+ jmxUrl.append('/');
+ }
+ url.append(path);
+ jmxUrl.append(path);
+
+ if (context != null) {
+ env.put(KubernetesJmxConnector.KUBERNETES_CLIENT_CONTEXT, context);
+ }
+ J4pClient jvmClient = KubernetesJmxConnector.probeProxyPath(env, client, url, headers);
+ if (jvmClient != null) {
+ JmcKubernetesJmxConnection connection;
+ try {
+ connection = new JmcKubernetesJmxConnection(jvmClient);
+ JVMDescriptor jvmDescriptor = JolokiaAgentDescriptor.attemptToGetJvmInfo(connection);
+ JMXServiceURL jmxServiceURL = new JMXServiceURL(jmxUrl.toString());
+ KubernetesJvmDescriptor descriptor = new KubernetesJvmDescriptor(metadata, jvmDescriptor, jmxServiceURL,
+ env);
+ found.put(descriptor.getGUID(), descriptor);
+ } catch (IOException e) {
+ parameters.logError(Messages.KubernetesDiscoveryListener_ErrConnectingToJvm, e);
+
+ }
+ }
+ }
+
+ private String getValueOrAttribute(String configValue, ObjectMeta metadata) {
+ if (notEmpty(configValue)) {
+ Matcher pattern = ATTRIBUTE_PATTERN.matcher(configValue);
+ if (pattern.find()) {
+ return metadata.getAnnotations().get(pattern.group("annotationName")); //$NON-NLS-1$
+ } else {
+ return configValue;// the default is to use config value as is
+ }
+ }
+ return null;
+ }
+
+ private void authorize(
+ HashMap headers, KubernetesClient client, String username, String password, String namespace,
+ Map jmxEnv) {
+
+ final Matcher userNameMatcher = SECRET_PATTERN.matcher(username);
+ String secretName = null;
+ Map secretValues = null;
+ if (userNameMatcher.find()) {
+ secretName = userNameMatcher.group("secretName"); //$NON-NLS-1$
+ secretValues = findSecret(client, namespace, secretName);
+ username = secretValues.get(userNameMatcher.group("itemName")); //$NON-NLS-1$
+ }
+
+ final Matcher passwordMatcher = SECRET_PATTERN.matcher(password);
+ if (passwordMatcher.find()) {
+ if (!secretName.equals(passwordMatcher.group("secretName"))) { //$NON-NLS-1$
+ secretValues = findSecret(client, namespace, passwordMatcher.group("secretName")); //$NON-NLS-1$
+ }
+ password = secretValues.get(passwordMatcher.group("itemName")); //$NON-NLS-1$
+ }
+
+ headers.put(AuthorizationHeaderParser.JOLOKIA_ALTERNATE_AUTHORIZATION_HEADER,
+ "Basic " + Base64Util.encode((username + ":" + password).getBytes())); //$NON-NLS-1$ //$NON-NLS-2$
+ jmxEnv.put(JMXConnector.CREDENTIALS, new String[] {username, password});
+
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private Map findSecret(KubernetesClient client, String namespace, String secretName) {
+
+ for (Secret secret : client.secrets().inNamespace(namespace).list().getItems()) {
+ if (secret.getMetadata().getName().equals(secretName)) {
+ if ("kubernetes.io/basic-auth".equals(secret.getType())) { //$NON-NLS-1$
+ Map data = secret.getData();
+ data.replaceAll((key, value) -> new String(Base64.decodeBase64(value)));
+ return data;
+ } else if ("Opaque".equals(secret.getType())) { //$NON-NLS-1$
+ for (Entry entry : secret.getData().entrySet()) {
+ if (entry.getKey().endsWith(".properties")) { //$NON-NLS-1$
+ try {
+ Properties properties = new Properties();
+ properties.load(new ByteArrayInputStream(Base64.decodeBase64(entry.getValue())));
+ return (Map) properties;
+ } catch (IOException ignore) {
+ }
+ }
+ }
+ }
+ }
+
+ }
+ throw new NoSuchElementException(Messages.KubernetesDiscoveryListener_CouldNotFindSecret + secretName
+ + Messages.KubernetesDiscoveryListener_InNamespace + namespace);
+
+ }
+
+ @Override
+ protected boolean isEnabled() {
+ return this.settings.scanForInstances();
+ }
+}
diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java
new file mode 100644
index 000000000..e57624274
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.kubernetes;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.management.remote.JMXServiceURL;
+
+import org.jolokia.kubernetes.client.KubernetesJmxConnector;
+import org.openjdk.jmc.jolokia.ServerConnectionDescriptor;
+import org.openjdk.jmc.common.jvm.JVMDescriptor;
+
+import io.fabric8.kubernetes.api.model.ObjectMeta;
+
+public class KubernetesJvmDescriptor implements ServerConnectionDescriptor {
+
+ private final JVMDescriptor jvmDescriptor;
+ private final ObjectMeta metadata;
+ private final Map env;
+ private final JMXServiceURL connectUrl;
+
+ public KubernetesJvmDescriptor(ObjectMeta metadata, JVMDescriptor jvmDescriptor, JMXServiceURL connectUrl,
+ Map env) {
+ this.jvmDescriptor = jvmDescriptor;
+ this.metadata = metadata;
+ this.env = env;
+ this.connectUrl = connectUrl;
+ }
+
+ @Override
+ public String getGUID() {
+ return this.metadata.getName();
+ }
+
+ @Override
+ public String getDisplayName() {
+ return this.metadata.getName();
+ }
+
+ @Override
+ public JVMDescriptor getJvmInfo() {
+ return this.jvmDescriptor;
+ }
+
+ public String getPath() {
+ String namespace = metadata.getNamespace();
+ final Object context = this.env.get(KubernetesJmxConnector.KUBERNETES_CLIENT_CONTEXT);
+ if (context != null) {
+ return context + "/" + namespace; //$NON-NLS-1$
+ }
+ return namespace;
+ }
+
+ @Override
+ public JMXServiceURL createJMXServiceURL() throws IOException {
+ return this.connectUrl;
+ }
+
+ @Override
+ public Map getEnvironment() {
+ return this.env;
+ }
+
+ @Override
+ public JMXServiceURL serviceUrl() {
+ return this.connectUrl;
+ }
+
+}
diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java
new file mode 100644
index 000000000..0f3a3b3aa
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.kubernetes;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.openjdk.jmc.kubernetes.messages"; //$NON-NLS-1$
+ public static String KubernetesDiscoveryListener_CouldNotFindSecret;
+ public static String KubernetesDiscoveryListener_Description;
+ public static String KubernetesDiscoveryListener_ErrConnectingToJvm;
+ public static String KubernetesDiscoveryListener_HttpOrHttps;
+ public static String KubernetesDiscoveryListener_InNamespace;
+ public static String KubernetesDiscoveryListener_JolokiaProtocol;
+ public static String KubernetesDiscoveryListener_MustProvidePassword;
+ public static String KubernetesDiscoveryListener_UnableToFindContexts;
+ public static String KubernetesDiscoveryListener_UnableToScan;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/messages.properties b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/messages.properties
new file mode 100644
index 000000000..ea0166360
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/messages.properties
@@ -0,0 +1,9 @@
+KubernetesDiscoveryListener_CouldNotFindSecret=Could not find secret named
+KubernetesDiscoveryListener_Description=List JVM in kubernetes cluster
+KubernetesDiscoveryListener_ErrConnectingToJvm=Error connecting to JVM in pod
+KubernetesDiscoveryListener_HttpOrHttps=' must be either 'http' or 'https'
+KubernetesDiscoveryListener_InNamespace=\ in namespace
+KubernetesDiscoveryListener_JolokiaProtocol=Jolokia protocol '
+KubernetesDiscoveryListener_MustProvidePassword=Password must be specified when username is specified
+KubernetesDiscoveryListener_UnableToFindContexts=Unable to find all kubernetes contexts
+KubernetesDiscoveryListener_UnableToScan=Unable to scan kubernetes context
diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java
new file mode 100644
index 000000000..30e0b16c5
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.kubernetes.preferences;
+
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.logging.Level;
+
+import org.eclipse.jface.preference.BooleanFieldEditor;
+import org.eclipse.jface.preference.FieldEditor;
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.jface.preference.StringFieldEditor;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.openjdk.jmc.common.security.CredentialsNotAvailableException;
+import org.openjdk.jmc.common.security.SecurityException;
+import org.openjdk.jmc.kubernetes.JmcKubernetesPlugin;
+
+/**
+ * This class represents a preference page that is contributed to the Preferences dialog. By
+ * subclassing FieldEditorPreferencePage, we can use the field support built into JFace
+ * that allows us to create a page that is small and knows how to save, restore and apply itself.
+ *
+ * This page is used to modify preferences only. They are stored in the preference store that
+ * belongs to the main plug-in class. That way, preferences can be accessed directly via the
+ * preference store.
+ */
+public class JmcKubernetesPreferenceForm extends FieldEditorPreferencePage
+ implements IWorkbenchPreferencePage, PreferenceConstants {
+
+ private Map dependantControls = new WeakHashMap<>();
+ private Text userField;
+ private Text passwordField;
+ private boolean credentialsDirty;
+
+ public JmcKubernetesPreferenceForm() {
+ super(GRID);
+ setPreferenceStore(JmcKubernetesPlugin.getDefault().getPreferenceStore());
+ setDescription(Messages.JmcKubernetesPreferenceForm_FormDescription);
+ }
+
+ /**
+ * Creates the field editors. Field editors are abstractions of the common GUI blocks needed to
+ * manipulate various types of preferences. Each field editor knows how to save and restore
+ * itself.
+ */
+ public void createFieldEditors() {
+ BooleanFieldEditor mainEnabler = new BooleanFieldEditor(P_SCAN_FOR_INSTANCES,
+ Messages.JmcKubernetesPreferenceForm_ScanForPods, getFieldEditorParent()) {
+ @Override
+ protected void valueChanged(boolean oldValue, boolean newValue) {
+ super.valueChanged(oldValue, newValue);
+ enableDependantFields(newValue);
+ }
+ };
+ addField(mainEnabler);
+
+ BooleanFieldEditor scanContextsEditor = new BooleanFieldEditor(P_SCAN_ALL_CONTEXTS,
+ Messages.JmcKubernetesPreferenceForm_AllContexts, getFieldEditorParent());
+ this.addDependantField(scanContextsEditor, scanContextsEditor.getDescriptionControl(getFieldEditorParent()));
+ this.addTextField(new StringFieldEditor(P_REQUIRE_LABEL, Messages.JmcKubernetesPreferenceForm_RequireLabel,
+ getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_LabelToolTip);
+ this.addTextField(new StringFieldEditor(P_JOLOKIA_PATH, Messages.JmcKubernetesPreferenceForm_PathLabel,
+ getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_PathTooltip);
+ this.addTextField(new StringFieldEditor(P_JOLOKIA_PORT, Messages.JmcKubernetesPreferenceForm_PortLabel,
+ getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_PortTooltip);
+ this.addTextField(new StringFieldEditor(P_JOLOKIA_PROTOCOL, Messages.JmcKubernetesPreferenceForm_ProtocolLabel,
+ getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_ProtocolTooltip);
+ createCredentialFields();
+ // set initial enablement
+ enableDependantFields(JmcKubernetesPlugin.getDefault().scanForInstances());
+
+ }
+
+ private void createCredentialFields() {
+ Label userLabel = new Label(getFieldEditorParent(), SWT.NONE);
+ userLabel.setText(Messages.JmcKubernetesPreferenceForm_UsernameLabel);
+ userLabel.setLayoutData(new GridData());
+ this.userField = new Text(getFieldEditorParent(), SWT.SINGLE | SWT.BORDER);
+ userField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ userField.setToolTipText(Messages.JmcKubernetesPreferenceForm_UsernameTooltip);
+ this.dependantControls.put(userField, null);
+
+ Label passLabel = new Label(getFieldEditorParent(), SWT.NONE);
+ passLabel.setText(Messages.JmcKubernetesPreferenceForm_PasswordLabel);
+ passLabel.setLayoutData(new GridData());
+ this.passwordField = new Text(getFieldEditorParent(), SWT.PASSWORD | SWT.SINGLE | SWT.BORDER);
+ passwordField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ JmcKubernetesPlugin plugin = JmcKubernetesPlugin.getDefault();
+
+ try {
+ userField.setText(plugin.username());
+ passwordField.setText(plugin.password());
+ } catch (CredentialsNotAvailableException ignore) {
+ // ignore if credentials are not stored
+ } catch (SecurityException e) {
+ plugin.getLogger().log(Level.WARNING, "Could not load kubernetes credentials", e); //$NON-NLS-1$
+ }
+
+ ModifyListener markCredentials = e -> credentialsDirty = true;
+ this.userField.addModifyListener(markCredentials);
+ this.passwordField.addModifyListener(markCredentials);
+ }
+
+ private void addTextField(StringFieldEditor field, String tooltip) {
+ Text textControl = field.getTextControl(getFieldEditorParent());
+ this.addDependantField(field, textControl);
+ textControl.setToolTipText(tooltip);
+ field.getLabelControl(getFieldEditorParent()).setToolTipText(tooltip);
+
+ }
+
+ private void addDependantField(FieldEditor field, Control control) {
+ this.dependantControls.put(control, null);
+ addField(field);
+ }
+
+ private void enableDependantFields(boolean enabled) {
+ for (Control field : this.dependantControls.keySet()) {
+ field.setEnabled(enabled);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
+ */
+ public void init(IWorkbench workbench) {
+ }
+
+ @Override
+ public boolean performOk() {
+ updateCredentialsIfApplicable();
+ return super.performOk();
+ }
+
+ private void updateCredentialsIfApplicable() {
+ if (this.credentialsDirty) {
+ try {
+ JmcKubernetesPlugin.getDefault().storeCredentials(userField.getText(), passwordField.getText());
+ this.credentialsDirty = false;
+ } catch (SecurityException ex) {
+ JmcKubernetesPlugin.getDefault().getLogger().log(Level.WARNING,
+ "Could not store kubernetes credentials", ex); //$NON-NLS-1$
+ }
+ }
+ }
+
+}
diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java
new file mode 100644
index 000000000..ea41c79d8
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.kubernetes.preferences;
+
+import org.openjdk.jmc.common.security.ICredentials;
+import org.openjdk.jmc.common.security.SecurityException;
+
+public interface KubernetesScanningParameters {
+ boolean scanForInstances();
+
+ boolean scanAllContexts();
+
+ String jolokiaPort();
+
+ String username() throws SecurityException;
+
+ String password() throws SecurityException;
+
+ String jolokiaPath();
+
+ String jolokiaProtocol();
+
+ String requireLabel();
+
+ ICredentials storeCredentials(String username, String password) throws SecurityException;
+
+ void logError(String message, Throwable error);
+}
diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java
new file mode 100644
index 000000000..f4b2a4ff9
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.kubernetes.preferences;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.openjdk.jmc.kubernetes.preferences.messages"; //$NON-NLS-1$
+ public static String JmcKubernetesPreferenceForm_AllContexts;
+ public static String JmcKubernetesPreferenceForm_FormDescription;
+ public static String JmcKubernetesPreferenceForm_LabelToolTip;
+ public static String JmcKubernetesPreferenceForm_PasswordLabel;
+ public static String JmcKubernetesPreferenceForm_PasswordTooltip;
+ public static String JmcKubernetesPreferenceForm_PathLabel;
+ public static String JmcKubernetesPreferenceForm_PathTooltip;
+ public static String JmcKubernetesPreferenceForm_PortLabel;
+ public static String JmcKubernetesPreferenceForm_PortTooltip;
+ public static String JmcKubernetesPreferenceForm_ProtocolLabel;
+ public static String JmcKubernetesPreferenceForm_ProtocolTooltip;
+ public static String JmcKubernetesPreferenceForm_RequireLabel;
+ public static String JmcKubernetesPreferenceForm_ScanForPods;
+ public static String JmcKubernetesPreferenceForm_UsernameTooltip;
+ public static String JmcKubernetesPreferenceForm_UsernameLabel;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java
new file mode 100644
index 000000000..1770dcf89
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.kubernetes.preferences;
+
+/**
+ * Constant definitions for plug-in preferences
+ */
+public interface PreferenceConstants {
+
+ public static final String P_SCAN_FOR_INSTANCES = "scanForInstances"; //$NON-NLS-1$
+ public static final String P_SCAN_ALL_CONTEXTS = "scanAllContexts"; //$NON-NLS-1$
+ public static final String P_REQUIRE_LABEL = "requireLabel"; //$NON-NLS-1$
+ public static final String P_JOLOKIA_PATH = "jolokiaPath"; //$NON-NLS-1$
+ public static final String P_JOLOKIA_PORT = "jolokiaPort"; //$NON-NLS-1$
+ public static final String P_JOLOKIA_PROTOCOL = "jolokiaProtocol"; //$NON-NLS-1$
+ public static final String P_CREDENTIALS_KEY = "kubernetes.scanning.credentials"; //$NON-NLS-1$
+}
diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java
new file mode 100644
index 000000000..203c567b9
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.kubernetes.preferences;
+
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.openjdk.jmc.kubernetes.JmcKubernetesPlugin;
+
+/**
+ * Class used to initialize default preference values.
+ */
+public class PreferenceInitializer extends AbstractPreferenceInitializer implements PreferenceConstants {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer#
+ * initializeDefaultPreferences()
+ */
+ public void initializeDefaultPreferences() {
+ IPreferenceStore store = JmcKubernetesPlugin.getDefault().getPreferenceStore();
+ store.setDefault(P_SCAN_FOR_INSTANCES, false);
+ store.setDefault(P_SCAN_ALL_CONTEXTS, false);
+ store.setDefault(P_REQUIRE_LABEL, "jolokia"); //$NON-NLS-1$
+ store.setDefault(P_JOLOKIA_PATH, "/jolokia/"); //$NON-NLS-1$
+ store.setDefault(P_JOLOKIA_PORT, "8778"); //$NON-NLS-1$
+ }
+
+}
diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/messages.properties b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/messages.properties
new file mode 100644
index 000000000..44b18279a
--- /dev/null
+++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/messages.properties
@@ -0,0 +1,17 @@
+JmcKubernetesPreferenceForm_AllContexts=Scan pods from all locally configured &contexts, if false: only scan the current contexts
+JmcKubernetesPreferenceForm_FormDescription=Options that allows you to scan kubernetes for JVMs running Jolokia\
+\n\nThe Kubernetes Proxy API will be used to attempt to access Jolokia like this (parenthesis denote optional components): \
+\n\n$kubernetesBaseUrl/api/v1/namespaces/podNamespace/pods/(jolokiaProtocol:)podname(:jolokiaPort)/proxy/jolokiaPath\n\n
+JmcKubernetesPreferenceForm_LabelToolTip=Only attempt to connect to pods with this label set, leave empty to try to scan all pods
+JmcKubernetesPreferenceForm_PasswordLabel=Require pass&word
+JmcKubernetesPreferenceForm_PasswordTooltip=Password , alternatively use ${kubernetes/secret/secretName/secretItem} where the secret is in the same namespace as the pod and the type is either kubernetes.io/basic-auth or Opaque with java.util.Properties compatible values
+JmcKubernetesPreferenceForm_PathLabel=Jolokia &path in pods
+JmcKubernetesPreferenceForm_PathTooltip=Use this path for jolokia, or specify ${kubernetes/annotation/annotationName} to be able to to get port name from pod metadata annotation of your choice
+JmcKubernetesPreferenceForm_PortLabel=Jolokia p&ort in pods
+JmcKubernetesPreferenceForm_PortTooltip=Port to use, leave empty to use default port of Kubernetes proxy, hardcode a port value, or alternatively ${kubernetes/annotation/annotationName} to get port name from pod metadata annotation of your choice
+JmcKubernetesPreferenceForm_ProtocolLabel=Jolokia pro&tocol in pods
+JmcKubernetesPreferenceForm_ProtocolTooltip=Protocol to use (optional), will infer http if not set, or hardcode to https, or alternatively ${kubernetes/annotation/annotationName} to get port name from pod metadata annotation of your choice
+JmcKubernetesPreferenceForm_RequireLabel=Require &label to scan pod
+JmcKubernetesPreferenceForm_ScanForPods=&Scan for kubernetes pods with Jolokia support
+JmcKubernetesPreferenceForm_UsernameTooltip=Username , alternatively use ${kubernetes/secret/secretName/secretItem} where the secret is in the same namespace as the pod and the type is either kubernetes.io/basic-auth or Opaque with java.util.Properties compatible values
+JmcKubernetesPreferenceForm_UsernameLabel=Require &username
diff --git a/application/pom.xml b/application/pom.xml
index 7029bf0db..101786fcb 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -44,6 +44,7 @@
-XX:+FlightRecorder${project.basedir}/../configuration
+ 1.7.2org.openjdk.jmc.alert
@@ -113,6 +114,8 @@
org.openjdk.jmc.ui.celleditorsorg.openjdk.jmc.updatesite.ideorg.openjdk.jmc.updatesite.rcp
+ org.openjdk.jmc.jolokia
+ org.openjdk.jmc.kubernetesl10ntests
diff --git a/application/tests/org.openjdk.jmc.jolokia.test/.classpath b/application/tests/org.openjdk.jmc.jolokia.test/.classpath
new file mode 100644
index 000000000..4432907f4
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.jolokia.test/.classpath
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/application/tests/org.openjdk.jmc.jolokia.test/META-INF/MANIFEST.MF b/application/tests/org.openjdk.jmc.jolokia.test/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..043e657d1
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.jolokia.test/META-INF/MANIFEST.MF
@@ -0,0 +1,17 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: RJMX Test
+Bundle-SymbolicName: org.openjdk.jmc.jolokia.test;singleton:=true
+Bundle-Version: 9.0.0.qualifier
+Bundle-Vendor: Oracle Corporation
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-ActivationPolicy: lazy
+Require-Bundle: org.junit,
+ org.openjdk.jmc.jolokia,
+ org.eclipse.osgi;bundle-version="3.16.200",
+ org.eclipse.ui.workbench,
+ org.openjdk.jmc.rjmx,
+ org.eclipse.ui,
+ org.awaitility,
+ org.hamcrest;bundle-version="2.1.0"
+Automatic-Module-Name: org.openjdk.jmc.jolokia.test
diff --git a/application/tests/org.openjdk.jmc.jolokia.test/build.properties b/application/tests/org.openjdk.jmc.jolokia.test/build.properties
new file mode 100644
index 000000000..a8bc8160a
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.jolokia.test/build.properties
@@ -0,0 +1,36 @@
+#
+# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2021, Kantega AS. All rights reserved.
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# The contents of this file are subject to the terms of either the Universal Permissive License
+# v 1.0 as shown at http://oss.oracle.com/licenses/upl
+#
+# or the following license:
+#
+# Redistribution and use in source and binary forms, with or without modification, are permitted
+# provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+# and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+# conditions and the following disclaimer in the documentation and/or other materials provided with
+# the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+# endorse or promote products derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+# WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+source.. = src/test/java/,\
+ src/test/resources/
+output.. = target/test-classes/
+bin.includes = META-INF/,.
diff --git a/application/tests/org.openjdk.jmc.jolokia.test/pom.xml b/application/tests/org.openjdk.jmc.jolokia.test/pom.xml
new file mode 100644
index 000000000..6b35d110a
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.jolokia.test/pom.xml
@@ -0,0 +1,77 @@
+
+
+
+ 4.0.0
+
+ org.openjdk.jmc
+ missioncontrol.application.tests
+ ${revision}${changelist}
+
+ org.openjdk.jmc.jolokia.test
+ eclipse-test-plugin
+
+
+ ${project.basedir}/../../../configuration
+
+
+
+
+ org.awaitility
+ awaitility
+ 4.0.0
+
+
+
+
+
+
+ org.eclipse.tycho
+ tycho-surefire-plugin
+ ${tycho.version}
+
+ ${test.includes}
+
+
+ -javaagent:${settings.localRepository}/org/jolokia/jolokia-jvm/${jolokia.version}/jolokia-jvm-${jolokia.version}.jar=port=0,discover=true
+
+
+
+
+
+
diff --git a/application/tests/org.openjdk.jmc.jolokia.test/src/test/java/org/openjdk/jmc/jolokia/JolokiaTest.java b/application/tests/org.openjdk.jmc.jolokia.test/src/test/java/org/openjdk/jmc/jolokia/JolokiaTest.java
new file mode 100644
index 000000000..d51bc9cc7
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.jolokia.test/src/test/java/org/openjdk/jmc/jolokia/JolokiaTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.jolokia;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanServerConnection;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.OperationsException;
+import javax.management.ReflectionException;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXServiceURL;
+
+import org.awaitility.Awaitility;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openjdk.jmc.common.IDescribable;
+import org.openjdk.jmc.jolokia.preferences.PreferenceConstants;
+import org.openjdk.jmc.rjmx.IConnectionDescriptor;
+import org.openjdk.jmc.rjmx.IServerDescriptor;
+import org.openjdk.jmc.rjmx.descriptorprovider.IDescriptorListener;
+
+/**
+ * I test that JMX connections done with JmcJolokiaJmxConnectionProvider are functional
+ */
+@SuppressWarnings("restriction")
+public class JolokiaTest {
+
+ static String jolokiaUrl;
+
+ private static Set unsafeAttributes = new HashSet<>(
+ Arrays.asList("BootClassPath", "UsageThreshold", "UsageThresholdExceeded", "UsageThresholdCount",
+ "CollectionUsageThreshold", "CollectionUsageThresholdExceeded", "CollectionUsageThresholdCount"));
+
+ private static JolokiaDiscoveryListener discoveryListener;
+
+ private static MBeanServerConnection jolokiaConnection;
+
+ @BeforeClass
+ public static void startServer() throws Exception {
+ // wait for Jolokia to be ready before commencing tests
+ Awaitility.await().atMost(Duration.ofSeconds(15))//Note: hard code property to avoid module dependency on agent
+ .until(() -> (jolokiaUrl = System.getProperty("jolokia.agent")) != null);
+ discoveryListener = new JolokiaDiscoveryListener();
+ jolokiaConnection = getJolokiaMBeanConnector();
+
+ }
+
+ @Test
+ public void testReadAttributesOverJolokia() throws MalformedURLException, IOException, OperationsException,
+ IntrospectionException, AttributeNotFoundException, ReflectionException, MBeanException {
+ for (ObjectName objectName : jolokiaConnection.queryNames(null, null)) {
+ for (MBeanAttributeInfo attributeInfo : getJolokiaMBeanConnector().getMBeanInfo(objectName)
+ .getAttributes()) {
+ if (!unsafeAttributes.contains(attributeInfo.getName())) {
+ System.out.println("Getting attribute " + objectName + "/" + attributeInfo.getName());
+ getJolokiaMBeanConnector().getAttribute(objectName, attributeInfo.getName());
+ }
+ }
+ }
+
+ }
+
+ @Test
+ public void testExecuteOperation() throws InstanceNotFoundException, MalformedObjectNameException, MBeanException,
+ ReflectionException, MalformedURLException, IOException {
+ jolokiaConnection.invoke(new ObjectName("java.lang:type=Memory"), "gc", new Object[0], new String[0]);
+ }
+
+ @Test
+ public void testWriteAttribute()
+ throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException,
+ MalformedObjectNameException, MBeanException, ReflectionException, MalformedURLException, IOException {
+ ObjectName objectName = new ObjectName("jolokia:type=Config");
+ String attribute = "Debug";
+ jolokiaConnection.setAttribute(objectName, new Attribute(attribute, true));
+ Assert.assertEquals(true, jolokiaConnection.getAttribute(objectName, attribute));
+
+ }
+
+ private static MBeanServerConnection getJolokiaMBeanConnector() throws IOException, MalformedURLException {
+ JMXConnector connector = new JmcJolokiaJmxConnectionProvider().newJMXConnector(
+ new JMXServiceURL(jolokiaUrl.replace("http", "service:jmx:jolokia")), Collections.emptyMap());
+ connector.connect();
+ MBeanServerConnection connection = connector.getMBeanServerConnection();
+ return connection;
+ }
+
+ @Test
+ public void testDiscover() {
+
+ final AtomicInteger foundVms = new AtomicInteger(0);
+
+ //Set config so that scanning takes place
+ InstanceScope.INSTANCE.getNode(JmcJolokiaPlugin.PLUGIN_ID).put(PreferenceConstants.P_SCAN, "true");
+
+ discoveryListener.addDescriptorListener(new IDescriptorListener() {
+ public void onDescriptorDetected(
+ IServerDescriptor serverDescriptor, String path, JMXServiceURL url,
+ IConnectionDescriptor connectionDescriptor, IDescribable provider) {
+ foundVms.getAndIncrement();
+ }
+
+ public void onDescriptorRemoved(String descriptorId) {
+ foundVms.getAndDecrement();
+ }
+
+ });
+ // Test that at least one VM (the one running the test was discovered)
+ Awaitility.await().atMost(Duration.ofSeconds(5)).until(() -> foundVms.get() > 0);
+ }
+
+ @AfterClass
+ public static void stopServer() throws Exception {
+ discoveryListener.shutdown();
+ }
+
+}
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/.classpath b/application/tests/org.openjdk.jmc.kubernetes.test/.classpath
new file mode 100644
index 000000000..cb04fe10d
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/.classpath
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF b/application/tests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..5913e76e3
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF
@@ -0,0 +1,20 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: RJMX Test
+Bundle-SymbolicName: org.openjdk.jmc.kubernetes.test;singleton:=true
+Bundle-Version: 9.0.0.qualifier
+Import-Package: io.fabric8.kubernetes.api.model
+Bundle-Vendor: Oracle Corporation
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-ActivationPolicy: lazy
+Require-Bundle: org.junit,
+ org.openjdk.jmc.jolokia,
+ org.eclipse.osgi;bundle-version="3.16.200",
+ org.eclipse.ui.workbench,
+ org.openjdk.jmc.rjmx;bundle-version="9.0.0",
+ org.eclipse.ui,
+ org.hamcrest;bundle-version="2.1",
+ org.openjdk.jmc.kubernetes;bundle-version="9.0.0",
+ com.github.tomakehurst.wiremock-standalone;bundle-version="2.27.2",
+ org.awaitility;bundle-version="4.0.0"
+Automatic-Module-Name: org.openjdk.jmc.jolokia.test
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/build.properties b/application/tests/org.openjdk.jmc.kubernetes.test/build.properties
new file mode 100644
index 000000000..a8bc8160a
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/build.properties
@@ -0,0 +1,36 @@
+#
+# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2021, Kantega AS. All rights reserved.
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# The contents of this file are subject to the terms of either the Universal Permissive License
+# v 1.0 as shown at http://oss.oracle.com/licenses/upl
+#
+# or the following license:
+#
+# Redistribution and use in source and binary forms, with or without modification, are permitted
+# provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+# and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+# conditions and the following disclaimer in the documentation and/or other materials provided with
+# the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+# endorse or promote products derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+# WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+source.. = src/test/java/,\
+ src/test/resources/
+output.. = target/test-classes/
+bin.includes = META-INF/,.
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/pom.xml b/application/tests/org.openjdk.jmc.kubernetes.test/pom.xml
new file mode 100644
index 000000000..b3e832633
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/pom.xml
@@ -0,0 +1,58 @@
+
+
+
+ 4.0.0
+
+ org.openjdk.jmc
+ missioncontrol.application.tests
+ ${revision}${changelist}
+
+ org.openjdk.jmc.kubernetes.test
+ eclipse-test-plugin
+
+
+ ${project.basedir}/../../../configuration
+
+
+
+
+ org.awaitility
+ awaitility
+ 4.0.0
+
+
+
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java
new file mode 100644
index 000000000..fa346077f
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Kantega AS. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The contents of this file are subject to the terms of either the Universal Permissive License
+ * v 1.0 as shown at http://oss.oracle.com/licenses/upl
+ *
+ * or the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided with
+ * the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jmc.kubernetes;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.time.Duration;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.MBeanException;
+import javax.management.MBeanServerConnection;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXServiceURL;
+
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.HttpClients;
+import org.awaitility.Awaitility;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.openjdk.jmc.common.IDescribable;
+import org.openjdk.jmc.common.jvm.JVMType;
+import org.openjdk.jmc.common.security.ICredentials;
+import org.openjdk.jmc.common.security.InMemoryCredentials;
+import org.openjdk.jmc.common.security.SecurityException;
+import org.openjdk.jmc.kubernetes.preferences.KubernetesScanningParameters;
+import org.openjdk.jmc.rjmx.IConnectionDescriptor;
+import org.openjdk.jmc.rjmx.IServerDescriptor;
+import org.openjdk.jmc.rjmx.descriptorprovider.IDescriptorListener;
+
+import com.github.tomakehurst.wiremock.client.WireMock;
+import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
+import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer;
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+
+/**
+ * Test that JMX connections done with JmcKubernetesJmxConnectionProvider are functional. In order
+ * to be able to test this in a contained environment, the kubernetes API is mocked with wiremock.
+ */
+@SuppressWarnings("restriction")
+public class JmcKubernetesTest {
+
+ static class TestParameters implements KubernetesScanningParameters {
+ public boolean scanForInstances, scanAllContexts;
+ public String jolokiaPort, jolokiaPath = "/jolokia/", jolokiaProtocol, requireLabel;
+ public InMemoryCredentials credentials;
+
+ @Override
+ public boolean scanForInstances() {
+ return this.scanForInstances;
+ }
+
+ @Override
+ public boolean scanAllContexts() {
+ return this.scanAllContexts;
+ }
+
+ @Override
+ public String jolokiaPort() {
+ return this.jolokiaPort;
+ }
+
+ @Override
+ public String username() throws SecurityException {
+ return this.credentials == null ? null : this.credentials.getUsername();
+ }
+
+ @Override
+ public String password() throws SecurityException {
+ return this.credentials == null ? null : this.credentials.getPassword();
+ }
+
+ @Override
+ public String jolokiaPath() {
+ return this.jolokiaPath;
+ }
+
+ @Override
+ public String jolokiaProtocol() {
+ return this.jolokiaProtocol;
+ }
+
+ @Override
+ public String requireLabel() {
+ return this.requireLabel;
+ }
+
+ @Override
+ public ICredentials storeCredentials(String username, String password) throws SecurityException {
+ return this.credentials = new InMemoryCredentials(username, password);
+ }
+
+ @Override
+ public void logError(String message, Throwable error) {
+ System.out.println(message);
+ error.printStackTrace(System.out);
+ }
+ }
+
+ @ClassRule
+ public static WireMockRule wiremock = new WireMockRule(
+ WireMockConfiguration.options().extensions(new ResponseTemplateTransformer(false)).port(0));
+
+ static final String jolokiaUrl = "service:jmx:kubernetes:///ns1/pod-abcdef/jolokia";
+
+ private static MBeanServerConnection jolokiaConnection;
+
+ @BeforeClass
+ public static void connect() throws Exception {
+ CloseableHttpResponse configResponse = HttpClients.createDefault()
+ .execute(new HttpGet(wiremock.baseUrl() + "/mock-kube-config.yml"));
+ Assert.assertEquals(configResponse.getStatusLine().getStatusCode(), 200);
+ File configFile = File.createTempFile("mock-kube-config", ".yml");
+ configResponse.getEntity().writeTo(new FileOutputStream(configFile));
+ // we set this so the KubernetesDiscoveryListener will work
+ //Setting taken from: https://github.com/fabric8io/kubernetes-client/blob/77a65f7d40f31a5dc37492cd9de3c317c2702fb4/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/Config.java#L120, unlikely to change
+ System.setProperty("kubeconfig", configFile.getAbsolutePath());
+ jolokiaConnection = getKubernetesMBeanConnector();
+ }
+
+ @Test
+ public void testExecuteOperation() throws InstanceNotFoundException, MalformedObjectNameException, MBeanException,
+ ReflectionException, MalformedURLException, IOException {
+ jolokiaConnection.invoke(new ObjectName("java.lang:type=Memory"), "gc", new Object[0], new String[0]);
+ }
+
+ @Test
+ public void testReadAttribute()
+ throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException,
+ MalformedObjectNameException, MBeanException, ReflectionException, MalformedURLException, IOException {
+ MBeanServerConnection jmxConnection = jolokiaConnection;
+ assertOneSingleAttribute(jmxConnection);
+
+ }
+
+ private void assertOneSingleAttribute(MBeanServerConnection jmxConnection) throws MalformedObjectNameException,
+ MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException, IOException {
+ ObjectName objectName = new ObjectName("java.lang:type=Memory");
+ String attribute = "Verbose";
+ Assert.assertEquals(false, jmxConnection.getAttribute(objectName, attribute));
+ }
+
+ @Before
+ public void reset() {
+ wiremock.resetAll();
+ wiremock.resetRequests();
+ }
+
+ private static MBeanServerConnection getKubernetesMBeanConnector() throws IOException, MalformedURLException {
+ JMXConnector connector = new JmcKubernetesJmxConnectionProvider().newJMXConnector(new JMXServiceURL(jolokiaUrl),
+ Collections.emptyMap());
+ connector.connect();
+ MBeanServerConnection connection = connector.getMBeanServerConnection();
+ return connection;
+ }
+
+ @Test
+ public void testDiscoverWithMostlyDefaultSettings() throws Exception {
+
+ TestParameters parameters = new TestParameters();
+ // Set config so that scanning takes place
+ parameters.scanForInstances = true;
+ testThatJvmIsFound(parameters);
+ }
+
+ @Test
+ public void testDiscoverWithPathFromAnnotation() throws Exception {
+ TestParameters parameters = new TestParameters();
+ parameters.scanForInstances = true;
+ parameters.jolokiaPath = "${kubernetes/annotation/jolokiaPath}";
+ testThatJvmIsFound(parameters);
+ }
+
+ @Test
+ public void testDiscoverWithPortFromAnnotation() throws Exception {
+ TestParameters parameters = new TestParameters();
+ parameters.scanForInstances = true;
+ parameters.jolokiaPort = "${kubernetes/annotation/jolokiaPort}";
+
+ testThatJvmIsFound(parameters);
+ }
+
+ @Test
+ public void testDiscoverWithBasicAuthFromSecret() throws Exception {
+ TestParameters parameters = new TestParameters();
+ parameters.scanForInstances = true;
+ parameters.credentials = new InMemoryCredentials("${kubernetes/secret/jolokia-auth/username}",
+ "${kubernetes/secret/jolokia-auth/password}");
+
+ testThatJvmIsFound(parameters);
+ // Verify that the expected authorization was picked up
+ WireMock.verify(WireMock
+ .postRequestedFor(WireMock.urlPathMatching("/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*"))
+ .withHeader("X-jolokia-authorization",
+ WireMock.equalTo("Basic " + Base64.getEncoder().encodeToString("admin:admin".getBytes()))));
+ }
+
+ @Test
+ public void testDiscoverWithAuthFromProperties() throws Exception {
+
+ TestParameters parameters = new TestParameters();
+ parameters.scanForInstances = true;
+ parameters.credentials = new InMemoryCredentials("${kubernetes/secret/jolokia-properties/user}",
+ "${kubernetes/secret/jolokia-properties/password}");
+
+ testThatJvmIsFound(parameters);
+ // Verify that the expected authorization was picked up
+ WireMock.verify(WireMock
+ .postRequestedFor(WireMock.urlPathMatching("/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*"))
+ .withHeader("X-jolokia-authorization",
+ WireMock.equalTo("Basic " + Base64.getEncoder().encodeToString("admin:secret".getBytes()))));
+ }
+
+ @Test
+ public void testDiscoverWithAuthDirectlyFromSettings() throws Exception {
+
+ TestParameters parameters = new TestParameters();
+ parameters.scanForInstances = true;
+ parameters.credentials = new InMemoryCredentials("user", "***");
+ testThatJvmIsFound(parameters);
+ // Verify that the expected authorization was picked up
+ WireMock.verify(WireMock
+ .postRequestedFor(WireMock.urlPathMatching("/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*"))
+ .withHeader("X-jolokia-authorization",
+ WireMock.equalTo("Basic " + Base64.getEncoder().encodeToString("user:***".getBytes()))));
+ }
+
+ private void testThatJvmIsFound(TestParameters parameters) throws Exception {
+
+ final KubernetesDiscoveryListener scanner = new KubernetesDiscoveryListener(parameters);
+ final Map foundVms = new HashMap<>();
+ IDescriptorListener descriptorListener = new IDescriptorListener() {
+ public void onDescriptorDetected(
+ IServerDescriptor serverDescriptor, String path, JMXServiceURL url,
+ IConnectionDescriptor connectionDescriptor, IDescribable provider) {
+ foundVms.put(serverDescriptor.getGUID(), serverDescriptor);
+ }
+
+ public void onDescriptorRemoved(String descriptorId) {
+ foundVms.remove(descriptorId);
+ }
+ };
+ scanner.addDescriptorListener(descriptorListener);
+
+ try {
+ // Test that at least one VM (the one running the test was discovered)
+
+ Awaitility.await().atMost(Duration.ofSeconds(5)).until(() -> !foundVms.isEmpty());
+ IServerDescriptor descriptor = foundVms.get("pod-abcdef");
+ Assert.assertNotNull(descriptor);
+ Assert.assertEquals(
+ "[JVMDescriptor] Java command: /Users/marska/Downloads/hawtio-app-2.9.1.jar --port 9090 PID: 88774",
+ descriptor.getJvmInfo().toString());
+ Assert.assertEquals(JVMType.HOTSPOT, descriptor.getJvmInfo().getJvmType());
+ Assert.assertEquals("18.0.1", descriptor.getJvmInfo().getJavaVersion());
+ Assert.assertTrue(descriptor instanceof IConnectionDescriptor);
+ IConnectionDescriptor connectDescriptor = (IConnectionDescriptor) descriptor;
+ JMXConnector connector = new JmcKubernetesJmxConnectionProvider()
+ .newJMXConnector(connectDescriptor.createJMXServiceURL(), connectDescriptor.getEnvironment());
+ connector.connect();
+ assertOneSingleAttribute(connector.getMBeanServerConnection());
+
+ } finally {
+ // Tell scanner thread to exit
+ scanner.shutdown();
+ }
+ }
+}
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json
new file mode 100644
index 000000000..4230121a7
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json
@@ -0,0 +1,10 @@
+{
+ "request" : {
+ "attribute" : "Verbose",
+ "mbean" : "java.lang:type=Memory",
+ "type" : "read"
+ },
+ "status" : 200,
+ "timestamp" : 1658563869,
+ "value" : false
+}
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json
new file mode 100644
index 000000000..b5bb351b7
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json
@@ -0,0 +1,19 @@
+{
+ "apiVersion": "v1",
+ "data": {
+ "password": "YWRtaW4=",
+ "username": "YWRtaW4="
+ },
+ "kind": "Secret",
+ "metadata": {
+ "annotations": {
+ "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"jolokia-auth\",\"namespace\":\"jfr\"},\"stringData\":{\"password\":\"admin\",\"username\":\"admin\"},\"type\":\"kubernetes.io/basic-auth\"}\n"
+ },
+ "creationTimestamp": "2022-05-20T13:59:12Z",
+ "name": "jolokia-auth",
+ "namespace": "jfr",
+ "resourceVersion": "130",
+ "uid": "e1563217-ef08-481d-b2e3-233fa3040b56"
+ },
+ "type": "kubernetes.io/basic-auth"
+}
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json
new file mode 100644
index 000000000..5bd35a69f
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json
@@ -0,0 +1,10 @@
+{
+ "request" : {
+ "mbean" : "java.lang:type=Memory",
+ "operation" : "gc()",
+ "type" : "exec"
+ },
+ "status" : 200,
+ "timestamp" : 1658567003,
+ "value" : null
+}
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json
new file mode 100644
index 000000000..e12bbd16c
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json
@@ -0,0 +1,15 @@
+{
+ "apiVersion": "v1",
+ "data": {
+ "jolokia.properties": "aG9zdD0wLjAuMC4wCnBhc3N3b3JkPXNlY3JldAp1c2VyPWFkbWluCnBvcnQ9ODc3OApkaXNjb3ZlcnlFbmFibGVkPXRydWUKZGlzY292ZXJ5QWdlbnRVcmw9aHR0cDovLyR7aG9zdH06ODc3OC9qb2xva2lhLwpwb2xpY3lMb2NhdGlvbj1jbGFzc3BhdGg6L0JPT1QtSU5GL2NsYXNzZXMvam9sb2tpYS1hY2Nlc3MueG1s"
+ },
+ "kind": "Secret",
+ "metadata": {
+ "creationTimestamp": "2022-05-20T13:59:12Z",
+ "name": "jolokia-properties",
+ "namespace": "jfr",
+ "resourceVersion": "147",
+ "uid": "025bf17a-cff5-46e6-8499-dd9d6de43fa9"
+ },
+ "type": "Opaque"
+}
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json
new file mode 100644
index 000000000..6299d0407
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json
@@ -0,0 +1,41 @@
+{
+ "request": {
+ "type": "version"
+ },
+ "value": {
+ "agent": "1.7.1",
+ "protocol": "7.2",
+ "config": {
+ "listenForHttpService": "true",
+ "authIgnoreCerts": "false",
+ "agentId": "192.168.1.104-28660-5bd82fed-servlet",
+ "debug": "false",
+ "agentType": "servlet",
+ "policyLocation": "classpath:\/jolokia-access.xml",
+ "agentContext": "\/jolokia",
+ "serializeException": "false",
+ "mimeType": "text\/plain",
+ "dispatcherClasses": "org.jolokia.http.Jsr160ProxyNotEnabledByDefaultAnymoreDispatcher",
+ "authMode": "basic",
+ "authMatch": "any",
+ "streaming": "true",
+ "canonicalNaming": "true",
+ "historyMaxEntries": "10",
+ "allowErrorDetails": "false",
+ "allowDnsReverseLookup": "true",
+ "realm": "jolokia",
+ "includeStackTrace": "false",
+ "restrictorClass": "io.hawt.system.RBACRestrictor",
+ "mbeanQualifier": "qualifier=hawtio",
+ "useRestrictorService": "false",
+ "debugMaxEntries": "100"
+ },
+ "info": {
+ "product": "jetty",
+ "vendor": "Eclipse",
+ "version": "9.4.z-SNAPSHOT"
+ }
+ },
+ "timestamp": 1658556959,
+ "status": 200
+}
\ No newline at end of file
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json
new file mode 100644
index 000000000..46d117f2e
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json
@@ -0,0 +1,46 @@
+{
+ "request" : {
+ "path" : "java.lang/type=Memory",
+ "type" : "list"
+ },
+ "status" : 200,
+ "timestamp" : 1658566168,
+ "value" : {
+ "attr" : {
+ "HeapMemoryUsage" : {
+ "desc" : "HeapMemoryUsage",
+ "rw" : false,
+ "type" : "javax.management.openmbean.CompositeData"
+ },
+ "NonHeapMemoryUsage" : {
+ "desc" : "NonHeapMemoryUsage",
+ "rw" : false,
+ "type" : "javax.management.openmbean.CompositeData"
+ },
+ "ObjectName" : {
+ "desc" : "ObjectName",
+ "rw" : false,
+ "type" : "javax.management.ObjectName"
+ },
+ "ObjectPendingFinalizationCount" : {
+ "desc" : "ObjectPendingFinalizationCount",
+ "rw" : false,
+ "type" : "int"
+ },
+ "Verbose" : {
+ "desc" : "Verbose",
+ "rw" : true,
+ "type" : "boolean"
+ }
+ },
+ "class" : "sun.management.MemoryImpl",
+ "desc" : "Information on the management interface of the MBean",
+ "op" : {
+ "gc" : {
+ "args" : [],
+ "desc" : "gc",
+ "ret" : "void"
+ }
+ }
+ }
+}
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml
new file mode 100644
index 000000000..f222f471a
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml
@@ -0,0 +1,17 @@
+apiVersion: v1
+clusters:
+- cluster:
+ server: {{request.baseUrl}}/
+ name: test
+contexts:
+- context:
+ cluster: test
+ user: test
+ name: test
+current-context: test
+kind: Config
+preferences: {}
+users:
+- name: test
+ user:
+ token: foobar
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json
new file mode 100644
index 000000000..77a61a3e3
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json
@@ -0,0 +1,11 @@
+{
+ "metadata": {
+ "clusterName": "test",
+ "labels": {
+ "jolokia": true
+ },
+ "name": "pod-abcdef",
+ "namespace": "ns1",
+ "selfLink": "/api/v1/namespaces/ns1/pods/pod-abcdef"
+ }
+}
\ No newline at end of file
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json
new file mode 100644
index 000000000..114edd364
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json
@@ -0,0 +1,19 @@
+{
+ "items": [
+ {
+ "metadata": {
+ "clusterName": "test",
+ "labels": {
+ "jolokia": true
+ },
+ "annotations": {
+ "jolokiaPort": 8778,
+ "jolokiaPath": "jolokia"
+ },
+ "name": "pod-abcdef",
+ "namespace": "ns1",
+ "selfLink": "/api/v1/namespaces/ns1/pods/pod-abcdef"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json
new file mode 100644
index 000000000..c096d78c5
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json
@@ -0,0 +1,104 @@
+{
+ "request" : {
+ "path" : "java.lang/type=Runtime",
+ "type" : "list"
+ },
+ "status" : 200,
+ "timestamp" : 1658564737,
+ "value" : {
+ "attr" : {
+ "BootClassPath" : {
+ "desc" : "BootClassPath",
+ "rw" : false,
+ "type" : "java.lang.String"
+ },
+ "BootClassPathSupported" : {
+ "desc" : "BootClassPathSupported",
+ "rw" : false,
+ "type" : "boolean"
+ },
+ "ClassPath" : {
+ "desc" : "ClassPath",
+ "rw" : false,
+ "type" : "java.lang.String"
+ },
+ "InputArguments" : {
+ "desc" : "InputArguments",
+ "rw" : false,
+ "type" : "[Ljava.lang.String;"
+ },
+ "LibraryPath" : {
+ "desc" : "LibraryPath",
+ "rw" : false,
+ "type" : "java.lang.String"
+ },
+ "ManagementSpecVersion" : {
+ "desc" : "ManagementSpecVersion",
+ "rw" : false,
+ "type" : "java.lang.String"
+ },
+ "Name" : {
+ "desc" : "Name",
+ "rw" : false,
+ "type" : "java.lang.String"
+ },
+ "ObjectName" : {
+ "desc" : "ObjectName",
+ "rw" : false,
+ "type" : "javax.management.ObjectName"
+ },
+ "Pid" : {
+ "desc" : "Pid",
+ "rw" : false,
+ "type" : "long"
+ },
+ "SpecName" : {
+ "desc" : "SpecName",
+ "rw" : false,
+ "type" : "java.lang.String"
+ },
+ "SpecVendor" : {
+ "desc" : "SpecVendor",
+ "rw" : false,
+ "type" : "java.lang.String"
+ },
+ "SpecVersion" : {
+ "desc" : "SpecVersion",
+ "rw" : false,
+ "type" : "java.lang.String"
+ },
+ "StartTime" : {
+ "desc" : "StartTime",
+ "rw" : false,
+ "type" : "long"
+ },
+ "SystemProperties" : {
+ "desc" : "SystemProperties",
+ "rw" : false,
+ "type" : "javax.management.openmbean.TabularData"
+ },
+ "Uptime" : {
+ "desc" : "Uptime",
+ "rw" : false,
+ "type" : "long"
+ },
+ "VmName" : {
+ "desc" : "VmName",
+ "rw" : false,
+ "type" : "java.lang.String"
+ },
+ "VmVendor" : {
+ "desc" : "VmVendor",
+ "rw" : false,
+ "type" : "java.lang.String"
+ },
+ "VmVersion" : {
+ "desc" : "VmVersion",
+ "rw" : false,
+ "type" : "java.lang.String"
+ }
+ },
+ "class" : "sun.management.RuntimeImpl",
+ "desc" : "Information on the management interface of the MBean"
+ }
+}
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json
new file mode 100644
index 000000000..ac68f94b5
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json
@@ -0,0 +1,43 @@
+{
+ "apiVersion": "v1",
+ "items": [
+ {
+ "apiVersion": "v1",
+ "data": {
+ "password": "YWRtaW4=",
+ "username": "YWRtaW4="
+ },
+ "kind": "Secret",
+ "metadata": {
+ "annotations": {
+ "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"jolokia-auth\",\"namespace\":\"ns1\"},\"stringData\":{\"password\":\"admin\",\"username\":\"admin\"},\"type\":\"kubernetes.io/basic-auth\"}\n"
+ },
+ "creationTimestamp": "2022-05-20T13:59:12Z",
+ "name": "jolokia-auth",
+ "namespace": "ns1",
+ "resourceVersion": "130",
+ "uid": "e1563217-ef08-481d-b2e3-233fa3040b56"
+ },
+ "type": "kubernetes.io/basic-auth"
+ },
+ {
+ "apiVersion": "v1",
+ "data": {
+ "jolokia.properties": "aG9zdD0wLjAuMC4wCnBhc3N3b3JkPXNlY3JldAp1c2VyPWFkbWluCnBvcnQ9ODc3OApkaXNjb3ZlcnlFbmFibGVkPXRydWUKZGlzY292ZXJ5QWdlbnRVcmw9aHR0cDovLyR7aG9zdH06ODc3OC9qb2xva2lhLwpwb2xpY3lMb2NhdGlvbj1jbGFzc3BhdGg6L0JPT1QtSU5GL2NsYXNzZXMvam9sb2tpYS1hY2Nlc3MueG1s"
+ },
+ "kind": "Secret",
+ "metadata": {
+ "creationTimestamp": "2022-05-20T13:59:12Z",
+ "name": "jolokia-properties",
+ "namespace": "ns1",
+ "resourceVersion": "147",
+ "uid": "025bf17a-cff5-46e6-8499-dd9d6de43fa9"
+ },
+ "type": "Opaque"
+ }
+ ],
+ "kind": "List",
+ "metadata": {
+ "resourceVersion": ""
+ }
+}
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json
new file mode 100644
index 000000000..dc2ff5543
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json
@@ -0,0 +1,101 @@
+[
+ {
+ "request" : {
+ "attribute" : "Pid",
+ "mbean" : "java.lang:type=Runtime",
+ "type" : "read"
+ },
+ "status" : 200,
+ "timestamp" : 1658586915,
+ "value" : 88774
+ },
+ {
+ "request" : {
+ "attribute" : "Name",
+ "mbean" : "java.lang:type=Runtime",
+ "type" : "read"
+ },
+ "status" : 200,
+ "timestamp" : 1658586915,
+ "value" : "88774@Martins-MacBook-Pro-2.local"
+ },
+ {
+ "request" : {
+ "attribute" : "InputArguments",
+ "mbean" : "java.lang:type=Runtime",
+ "type" : "read"
+ },
+ "status" : 200,
+ "timestamp" : 1658586915,
+ "value" : []
+ },
+ {
+ "request" : {
+ "attribute" : "SystemProperties",
+ "mbean" : "java.lang:type=Runtime",
+ "type" : "read"
+ },
+ "status" : 200,
+ "timestamp" : 1658586915,
+ "value" : {
+ "apple.awt.application.name" : "App",
+ "file.encoding" : "UTF-8",
+ "file.separator" : "/",
+ "ftp.nonProxyHosts" : "local|*.local|169.254/16|*.169.254/16",
+ "hawtio.authenticationEnabled" : "false",
+ "hawtio.redirect.scheme" : "http",
+ "hawtio.url" : "http://localhost:9090/hawtio",
+ "http.nonProxyHosts" : "local|*.local|169.254/16|*.169.254/16",
+ "java.class.path" : "/Users/marska/Downloads/hawtio-app-2.9.1.jar",
+ "java.class.version" : "62.0",
+ "java.home" : "/Library/Java/JavaVirtualMachines/temurin-18.jdk/Contents/Home",
+ "java.io.tmpdir" : "/var/folders/1f/cdm0073x1mj1swnhtw181_4m0000gn/T/",
+ "java.library.path" : "/Users/marska/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.",
+ "java.runtime.name" : "OpenJDK Runtime Environment",
+ "java.runtime.version" : "18.0.1+10",
+ "java.specification.name" : "Java Platform API Specification",
+ "java.specification.vendor" : "Oracle Corporation",
+ "java.specification.version" : "18",
+ "java.vendor" : "Eclipse Adoptium",
+ "java.vendor.url" : "https://adoptium.net/",
+ "java.vendor.url.bug" : "https://github.com/adoptium/adoptium-support/issues",
+ "java.vendor.version" : "Temurin-18.0.1+10",
+ "java.version" : "18.0.1",
+ "java.version.date" : "2022-04-19",
+ "java.vm.compressedOopsMode" : "Zero based",
+ "java.vm.info" : "mixed mode, sharing",
+ "java.vm.name" : "OpenJDK 64-Bit Server VM",
+ "java.vm.specification.name" : "Java Virtual Machine Specification",
+ "java.vm.specification.vendor" : "Oracle Corporation",
+ "java.vm.specification.version" : "18",
+ "java.vm.vendor" : "Eclipse Adoptium",
+ "java.vm.version" : "18.0.1+10",
+ "jdk.debug" : "release",
+ "line.separator" : "\n",
+ "native.encoding" : "UTF-8",
+ "org.eclipse.jetty.util.log.class" : "org.eclipse.jetty.util.log.Slf4jLog",
+ "os.arch" : "x86_64",
+ "os.name" : "Mac OS X",
+ "os.version" : "11.6.6",
+ "path.separator" : ":",
+ "socksNonProxyHosts" : "local|*.local|169.254/16|*.169.254/16",
+ "sun.arch.data.model" : "64",
+ "sun.awt.enableExtraMouseButtons" : "true",
+ "sun.boot.library.path" : "/Library/Java/JavaVirtualMachines/temurin-18.jdk/Contents/Home/lib",
+ "sun.cpu.endian" : "little",
+ "sun.io.unicode.encoding" : "UnicodeBig",
+ "sun.java.command" : "/Users/marska/Downloads/hawtio-app-2.9.1.jar --port 9090",
+ "sun.java.launcher" : "SUN_STANDARD",
+ "sun.jnu.encoding" : "UTF-8",
+ "sun.management.compiler" : "HotSpot 64-Bit Tiered Compilers",
+ "sun.stderr.encoding" : "UTF-8",
+ "sun.stdout.encoding" : "UTF-8",
+ "user.country" : "NO",
+ "user.dir" : "/Users/marska/Documents/dev/jmc/jmc8/releng/third-party",
+ "user.home" : "/Users/marska",
+ "user.language" : "nb",
+ "user.name" : "marska",
+ "user.timezone" : "Asia/Taipei"
+ }
+ }
+]
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json
new file mode 100644
index 000000000..6bb03ecec
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json
@@ -0,0 +1,15 @@
+{
+ "request": {
+ "method": "POST",
+ "urlPath": "/api/v1/namespaces/ns1/pods/pod-abcdef/proxy/jolokia",
+ "bodyPatterns": [
+ {
+ "equalToJson": "{\"operation\":\"gc()\",\"mbean\":\"java.lang:type=Memory\",\"type\":\"EXEC\"}"
+ }
+ ]
+ },
+ "response": {
+ "status": 200,
+ "bodyFileName": "jolokia-exec.json"
+ }
+}
\ No newline at end of file
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json
new file mode 100644
index 000000000..e4170948c
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json
@@ -0,0 +1,15 @@
+{
+ "request": {
+ "method": "POST",
+ "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*",
+ "bodyPatterns": [
+ {
+ "equalToJson": "{\"path\":\"java.lang/type=Memory\",\"type\":\"LIST\"}"
+ }
+ ]
+ },
+ "response": {
+ "status": 200,
+ "bodyFileName": "memory-list.json"
+ }
+}
\ No newline at end of file
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json
new file mode 100644
index 000000000..f936e7eb0
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json
@@ -0,0 +1,15 @@
+{
+ "request": {
+ "method": "POST",
+ "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*",
+ "bodyPatterns": [
+ {
+ "equalToJson": "{\"path\":\"java.lang/type=Runtime\",\"type\":\"LIST\"}"
+ }
+ ]
+ },
+ "response": {
+ "status": 200,
+ "bodyFileName": "runtime-list.json"
+ }
+}
\ No newline at end of file
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json
new file mode 100644
index 000000000..5b26da423
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json
@@ -0,0 +1,15 @@
+{
+ "request": {
+ "method": "POST",
+ "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*",
+ "bodyPatterns": [
+ {
+ "equalToJson": "{\"type\":\"version\"}"
+ }
+ ]
+ },
+ "response": {
+ "status": 200,
+ "bodyFileName": "jolokia-version.json"
+ }
+}
\ No newline at end of file
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json
new file mode 100644
index 000000000..98670016f
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json
@@ -0,0 +1,15 @@
+{
+ "request": {
+ "method": "POST",
+ "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*",
+ "bodyPatterns": [
+ {
+ "equalToJson": "{\"type\":\"VERSION\"}"
+ }
+ ]
+ },
+ "response": {
+ "status": 200,
+ "bodyFileName": "jolokia-version.json"
+ }
+}
\ No newline at end of file
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json
new file mode 100644
index 000000000..1584e3b4a
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json
@@ -0,0 +1,15 @@
+{
+ "request": {
+ "method": "POST",
+ "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*",
+ "bodyPatterns": [
+ {
+ "equalToJson": "{\"attribute\":\"Verbose\",\"mbean\":\"java.lang:type=Memory\",\"type\":\"READ\"}"
+ }
+ ]
+ },
+ "response": {
+ "status": 200,
+ "bodyFileName": "jolokia-attribute.json"
+ }
+}
\ No newline at end of file
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json
new file mode 100644
index 000000000..5b26da423
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json
@@ -0,0 +1,15 @@
+{
+ "request": {
+ "method": "POST",
+ "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*",
+ "bodyPatterns": [
+ {
+ "equalToJson": "{\"type\":\"version\"}"
+ }
+ ]
+ },
+ "response": {
+ "status": 200,
+ "bodyFileName": "jolokia-version.json"
+ }
+}
\ No newline at end of file
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json
new file mode 100644
index 000000000..418a88763
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json
@@ -0,0 +1,13 @@
+{
+ "request": {
+ "method": "GET",
+ "url": "/mock-kube-config.yml"
+ },
+ "response": {
+ "status": 200,
+ "bodyFileName": "mock-kube-config.yml",
+ "transformers": [
+ "response-template"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json
new file mode 100644
index 000000000..ebcbd7c5e
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json
@@ -0,0 +1,10 @@
+{
+ "request": {
+ "method": "GET",
+ "url": "/api/v1/namespaces/ns1/pods/pod-abcdef"
+ },
+ "response": {
+ "status": 200,
+ "bodyFileName": "pod.json"
+ }
+}
\ No newline at end of file
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json
new file mode 100644
index 000000000..061fb0f53
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json
@@ -0,0 +1,10 @@
+{
+ "request": {
+ "method": "GET",
+ "urlPath": "/api/v1/pods"
+ },
+ "response": {
+ "status": 200,
+ "bodyFileName": "pods.json"
+ }
+}
\ No newline at end of file
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json
new file mode 100644
index 000000000..113bb91e1
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json
@@ -0,0 +1,10 @@
+{
+ "request": {
+ "method": "GET",
+ "url": "/api/v1/namespaces/ns1/secrets"
+ },
+ "response": {
+ "status": 200,
+ "bodyFileName": "secrets.json"
+ }
+}
\ No newline at end of file
diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json
new file mode 100644
index 000000000..e3d1147b2
--- /dev/null
+++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json
@@ -0,0 +1,15 @@
+{
+ "request": {
+ "method": "POST",
+ "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*",
+ "bodyPatterns": [
+ {
+ "equalToJson": "[{\"attribute\":\"Pid\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"},{\"attribute\":\"Name\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"},{\"attribute\":\"InputArguments\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"},{\"attribute\":\"SystemProperties\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"}]"
+ }
+ ]
+ },
+ "response": {
+ "status": 200,
+ "bodyFileName": "system-attributes.json"
+ }
+}
\ No newline at end of file
diff --git a/application/tests/pom.xml b/application/tests/pom.xml
index b17b9ceea..da5f8cb80 100644
--- a/application/tests/pom.xml
+++ b/application/tests/pom.xml
@@ -54,6 +54,8 @@
org.openjdk.jmc.rjmx.testorg.openjdk.jmc.ui.common.testorg.openjdk.jmc.ui.test
+ org.openjdk.jmc.jolokia.test
+ org.openjdk.jmc.kubernetes.test**/Test*.java,**/*Test.java,**/*TestCase.java
diff --git a/releng/platform-definitions/platform-definition-2022-09/platform-definition-2022-09.target b/releng/platform-definitions/platform-definition-2022-09/platform-definition-2022-09.target
index 7e62ff50c..12ebe1c5f 100644
--- a/releng/platform-definitions/platform-definition-2022-09/platform-definition-2022-09.target
+++ b/releng/platform-definitions/platform-definition-2022-09/platform-definition-2022-09.target
@@ -50,6 +50,10 @@
+
+
+
+
diff --git a/releng/platform-definitions/platform-definition-2022-12/platform-definition-2022-12.target b/releng/platform-definitions/platform-definition-2022-12/platform-definition-2022-12.target
index 0508bf318..752b82c64 100644
--- a/releng/platform-definitions/platform-definition-2022-12/platform-definition-2022-12.target
+++ b/releng/platform-definitions/platform-definition-2022-12/platform-definition-2022-12.target
@@ -50,6 +50,10 @@
+
+
+
+
diff --git a/releng/platform-definitions/platform-definition-2023-03/platform-definition-2023-03.target b/releng/platform-definitions/platform-definition-2023-03/platform-definition-2023-03.target
index e323304f4..209732d96 100644
--- a/releng/platform-definitions/platform-definition-2023-03/platform-definition-2023-03.target
+++ b/releng/platform-definitions/platform-definition-2023-03/platform-definition-2023-03.target
@@ -45,6 +45,10 @@
+
+
+
+
diff --git a/releng/platform-definitions/platform-definition-2023-09/platform-definition-2023-09.target b/releng/platform-definitions/platform-definition-2023-09/platform-definition-2023-09.target
index fc382b0b9..ba2187584 100644
--- a/releng/platform-definitions/platform-definition-2023-09/platform-definition-2023-09.target
+++ b/releng/platform-definitions/platform-definition-2023-09/platform-definition-2023-09.target
@@ -45,6 +45,10 @@
+
+
+
+
diff --git a/releng/third-party/pom.xml b/releng/third-party/pom.xml
index d20fa15fc..80b8e61d1 100644
--- a/releng/third-party/pom.xml
+++ b/releng/third-party/pom.xml
@@ -59,6 +59,8 @@
2.0.010.0.121.3.6
+ 1.7.2
+ 2.27.20.0.1-rc36.0.0
@@ -135,6 +137,22 @@
org.apache.aries.spifly:org.apache.aries.spifly.dynamic.bundle:${spifly.version}
+
+ org.jolokia:jolokia-jmx-adapter:jar:standalone:${jolokia.version}
+ false
+
+
+ org.jolokia:jolokia-kubernetes:jar:standalone:${jolokia.version}
+ false
+
+
+ org.jolokia:jolokia-jvm:${jolokia.version}
+ false
+
+
+ com.github.tomakehurst:wiremock-standalone:${wiremock.version}
+ false
+ io.github.bric3.fireplace:fireplace-swing:${fireplace.version}