diff --git a/core-java-modules/core-java-perf/src/main/java/com/baeldung/jmxshell/JmxCalculatorMain.java b/core-java-modules/core-java-perf/src/main/java/com/baeldung/jmxshell/JmxCalculatorMain.java new file mode 100644 index 0000000000..1201f2551f --- /dev/null +++ b/core-java-modules/core-java-perf/src/main/java/com/baeldung/jmxshell/JmxCalculatorMain.java @@ -0,0 +1,25 @@ +package com.baeldung.jmxshell; + +import java.lang.management.ManagementFactory; +import java.util.Scanner; + +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import com.baeldung.jmxshell.bean.Calculator; + +public class JmxCalculatorMain { + + public static void main(String[] args) throws Exception { + MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + server.registerMBean(new Calculator(), new ObjectName("com.baeldung.jxmshell:type=basic,name=calculator")); + + System.out.printf("mbean registered. pid: %s\n", ManagementFactory.getRuntimeMXBean() + .getName()); + + try (Scanner scanner = new Scanner(System.in)) { + System.out.println(""); + scanner.nextLine(); + } + } +} diff --git a/core-java-modules/core-java-perf/src/main/java/com/baeldung/jmxshell/bean/Calculator.java b/core-java-modules/core-java-perf/src/main/java/com/baeldung/jmxshell/bean/Calculator.java new file mode 100644 index 0000000000..cb57a5e154 --- /dev/null +++ b/core-java-modules/core-java-perf/src/main/java/com/baeldung/jmxshell/bean/Calculator.java @@ -0,0 +1,32 @@ +package com.baeldung.jmxshell.bean; + +public class Calculator implements CalculatorMBean { + + private Integer a = 0; + private Integer b = 0; + + @Override + public Integer sum() { + return a + b; + } + + @Override + public Integer getA() { + return a; + } + + @Override + public void setA(Integer a) { + this.a = a; + } + + @Override + public Integer getB() { + return b; + } + + @Override + public void setB(Integer b) { + this.b = b; + } +} diff --git a/core-java-modules/core-java-perf/src/main/java/com/baeldung/jmxshell/bean/CalculatorMBean.java b/core-java-modules/core-java-perf/src/main/java/com/baeldung/jmxshell/bean/CalculatorMBean.java new file mode 100644 index 0000000000..16e104295a --- /dev/null +++ b/core-java-modules/core-java-perf/src/main/java/com/baeldung/jmxshell/bean/CalculatorMBean.java @@ -0,0 +1,14 @@ +package com.baeldung.jmxshell.bean; + +public interface CalculatorMBean { + + Integer sum(); + + Integer getA(); + + void setA(Integer a); + + Integer getB(); + + void setB(Integer b); +} diff --git a/core-java-modules/core-java-perf/src/main/java/com/baeldung/jmxshell/custom/JmxConnectionWrapper.java b/core-java-modules/core-java-perf/src/main/java/com/baeldung/jmxshell/custom/JmxConnectionWrapper.java new file mode 100644 index 0000000000..ea70fa3c96 --- /dev/null +++ b/core-java-modules/core-java-perf/src/main/java/com/baeldung/jmxshell/custom/JmxConnectionWrapper.java @@ -0,0 +1,68 @@ +package com.baeldung.jmxshell.custom; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.management.Attribute; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanInfo; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; + +public class JmxConnectionWrapper { + + private final Map attributeMap; + private final MBeanServerConnection connection; + private final ObjectName objectName; + + public JmxConnectionWrapper(String url, String beanName) throws Exception { + objectName = new ObjectName(beanName); + + connection = JMXConnectorFactory.connect(new JMXServiceURL(url)) + .getMBeanServerConnection(); + + MBeanInfo bean = connection.getMBeanInfo(objectName); + MBeanAttributeInfo[] attributes = bean.getAttributes(); + + this.attributeMap = Stream.of(attributes) + .peek(System.out::println) + .collect(Collectors.toMap(MBeanAttributeInfo::getName, Function.identity())); + } + + public boolean hasAttribute(String attributeName) { + return attributeMap.containsKey(attributeName); + } + + public Object attributeValue(String name, String value) throws Exception { + if (value != null) { + connection.setAttribute(objectName, new Attribute(name, parse(value))); + } + return connection.getAttribute(objectName, name); + } + + public Object invoke(String operation) throws Exception { + String[] signature = new String[] {}; + Object[] params = new Object[] {}; + + return connection.invoke(objectName, operation, params, signature); + } + + private static Object parse(String value) { + if (value == null) + return null; + + if (value.matches("\\d+")) { + return Integer.valueOf(value); + } else if (value.trim() + .toLowerCase() + .matches("true|false")) { + return Boolean.valueOf(value); + } + + return value.equals("null") ? null : value; + } +} diff --git a/core-java-modules/core-java-perf/src/main/java/com/baeldung/jmxshell/custom/JmxInvoker.java b/core-java-modules/core-java-perf/src/main/java/com/baeldung/jmxshell/custom/JmxInvoker.java new file mode 100644 index 0000000000..dda765289e --- /dev/null +++ b/core-java-modules/core-java-perf/src/main/java/com/baeldung/jmxshell/custom/JmxInvoker.java @@ -0,0 +1,34 @@ +package com.baeldung.jmxshell.custom; + +public class JmxInvoker { + + public static void main(String... args) throws Exception { + String serviceURL = args[0]; + String name = args[1]; + String operation = args[2]; + String attributeValue = null; + if (args.length > 3) { + attributeValue = args[3]; + } + + String result = execute(serviceURL, name, operation, attributeValue); + System.out.println(result); + } + + public static String execute(String url, String mBeanName, String operation, String attributeValue) { + try { + JmxConnectionWrapper connection = new JmxConnectionWrapper(url, mBeanName); + + if (connection.hasAttribute(operation)) { + Object value = connection.attributeValue(operation, attributeValue); + return operation + "=" + value; + } else { + Object result = connection.invoke(operation); + return operation + "(): " + result; + } + } catch (Exception e) { + e.printStackTrace(); + return e.getClass() + ": " + e.getMessage(); + } + } +} diff --git a/core-java-modules/core-java-perf/src/main/resources/jmxshell/jmx-invoker.sh b/core-java-modules/core-java-perf/src/main/resources/jmxshell/jmx-invoker.sh new file mode 100755 index 0000000000..cf9fa1621d --- /dev/null +++ b/core-java-modules/core-java-perf/src/main/resources/jmxshell/jmx-invoker.sh @@ -0,0 +1,45 @@ +#!/bin/sh +jar='/tmp/core-java-perf.jar' +address='localhost:11234' +mbean='com.baeldung.jxmshell:name=calculator,type=basic' +operation='sum' + +while test $# -gt 0 +do + case "$1" in + --jar) + shift + jar="$1" + ;; + --address) + shift + address="$1" + ;; + --mbean) + shift + mbean="$1" + ;; + --run|-x) + shift + operation="$1" + ;; + --get) + shift + operation="$1" + ;; + --set) + shift + operation="$1 $2" + ;; + -*) + echo "bad option '$1'" + exit 1 + ;; + esac + shift +done + +java -cp "$jar" com.baeldung.jmxshell.custom.JmxInvoker \ + "service:jmx:rmi:///jndi/rmi://$address/jmxrmi" \ + "$mbean" \ + "$operation" diff --git a/core-java-modules/core-java-perf/src/main/resources/jmxshell/jmxclient.sh b/core-java-modules/core-java-perf/src/main/resources/jmxshell/jmxclient.sh new file mode 100755 index 0000000000..72214ed181 --- /dev/null +++ b/core-java-modules/core-java-perf/src/main/resources/jmxshell/jmxclient.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +jar='/tmp/cmdline-jmxclient-0.10.3.jar' +address='localhost:11234' + +mbean='com.baeldung.jxmshell:name=calculator,type=basic' +operation='sum' +command="$mbean" + +while test $# -gt 0 +do + case "$1" in + --jar) + shift + jar="$1" + ;; + --address) + shift + address="$1" + ;; + --mbean) + shift + mbean="$1" + ;; + --run|-x) + shift + operation="$1" + + command="${mbean} ${operation}" + ;; + --set) + shift + operation="$1" + + shift + attribute_value="$1" + + command="${mbean} ${operation}=${attribute_value}" + ;; + --get) + shift + operation="$1" + + command="${mbean} ${operation}" + ;; + -*) + echo "bad option '$1'" + exit 1 + ;; + esac + shift +done + +java -jar "$jar" - "$address" "$command" diff --git a/core-java-modules/core-java-perf/src/main/resources/jmxshell/jmxterm.pl b/core-java-modules/core-java-perf/src/main/resources/jmxshell/jmxterm.pl new file mode 100755 index 0000000000..af199a4113 --- /dev/null +++ b/core-java-modules/core-java-perf/src/main/resources/jmxshell/jmxterm.pl @@ -0,0 +1,22 @@ +#!/usr/bin/perl + +$jar = "/tmp/jmxterm-1.0.4-uber.jar"; + +$host = "localhost"; +$port = 11234; + +$mbean = $ARGV[0] || "com.baeldung.jxmshell:name=calculator,type=basic"; +$operation = $ARGV[1] || "sum"; + +open JMX, "| java -jar $jar -n"; + +print JMX "open $host:$port\n"; +$attribute_value = $ARGV[2]; +if (defined $attribute_value) { + print JMX "set -b ${mbean} ${operation} ${attribute_value}\n"; +} else { + print JMX "run -b ${mbean} ${operation}\n"; +} +print JMX "close\n"; + +close JMX; diff --git a/core-java-modules/core-java-perf/src/main/resources/jmxshell/jmxterm.sh b/core-java-modules/core-java-perf/src/main/resources/jmxshell/jmxterm.sh new file mode 100755 index 0000000000..f81d0be920 --- /dev/null +++ b/core-java-modules/core-java-perf/src/main/resources/jmxshell/jmxterm.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +jar='/tmp/jmxterm-1.0.4-uber.jar' +address='localhost:11234' + +mbean='com.baeldung.jxmshell:name=calculator,type=basic' +operation='sum' +command="info -b $mbean" + +while test $# -gt 0 +do + case "$1" in + --jar|-j) + shift; jar="$1" + ;; + --address|-l) + shift; address="$1" + ;; + --mbean|-b) + shift; mbean="$1" + ;; + --run|-x) + shift; operation="$1" + + command="run -b ${mbean} ${operation}" + ;; + --set) + shift; operation="$1" + shift; attribute_value="$1" + + command="set -b ${mbean} ${operation} ${attribute_value}" + ;; + --get) + shift; operation="$1" + + command="get -b ${mbean} ${operation}" + ;; + -*) + echo "bad option '$1'" + exit 1 + ;; + esac + shift +done + +echo "$command" | java -jar "$jar" -l "$address" -n -v silent diff --git a/core-java-modules/core-java-perf/src/test/java/com/baeldung/jmxshell/custom/JmxInvokerLiveTest.java b/core-java-modules/core-java-perf/src/test/java/com/baeldung/jmxshell/custom/JmxInvokerLiveTest.java new file mode 100644 index 0000000000..2177e0a5ea --- /dev/null +++ b/core-java-modules/core-java-perf/src/test/java/com/baeldung/jmxshell/custom/JmxInvokerLiveTest.java @@ -0,0 +1,47 @@ +package com.baeldung.jmxshell.custom; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +@TestMethodOrder(OrderAnnotation.class) +class JmxInvokerLiveTest { + + private static final String JMX_URL = "service:jmx:rmi:///jndi/rmi://localhost:11234/jmxrmi"; + private static final String JMX_MBEAN_NAME = "com.baeldung.jxmshell:name=calculator,type=basic"; + private static final String ATTRIBUTE_A = "A"; + private static final String ATTRIBUTE_B = "B"; + private static final String SUM_OPERATION = "sum"; + private static final Integer ATTRIBUTE_VALUE = 1; + + @Test + @Order(1) + void givenAttributeValue_whenSetAttributeA_thenResultMatches() { + String attributeValue = ATTRIBUTE_VALUE.toString(); + + String result = JmxInvoker.execute(JMX_URL, JMX_MBEAN_NAME, ATTRIBUTE_A, attributeValue); + + assertEquals(ATTRIBUTE_A + "=" + attributeValue, result); + } + + @Test + @Order(2) + void givenAttributeValue_whenSetAttributeB_thenResultMatches() { + String attributeValue = ATTRIBUTE_VALUE.toString(); + + String result = JmxInvoker.execute(JMX_URL, JMX_MBEAN_NAME, ATTRIBUTE_B, attributeValue); + + assertEquals(ATTRIBUTE_B + "=" + attributeValue, result); + } + + @Test + @Order(3) + void whenSumOperation_thenSumIsCorrect() { + String result = JmxInvoker.execute(JMX_URL, JMX_MBEAN_NAME, SUM_OPERATION, null); + + assertEquals(SUM_OPERATION + "(): " + (ATTRIBUTE_VALUE + ATTRIBUTE_VALUE), result); + } +}