Revise reference documentation for Spring JMX annotations

This commit revises the reference documentation for Spring JMX
annotations for various reasons including, but not limited to, the
following.

- Type names such as ManagedResource are often ambiguous, especially
  when discussing an annotation like @⁠ManagedResource instead of
  org.springframework.jmx.export.metadata.ManagedResource which is a
  class.
- AnnotationTestBean implements IJmxTestBean, even though an annotated
  MBean is not required to implement any interfaces, and in fact the
  example is meant to demonstrate that an annotated POJO suffices.
- @⁠ManagedOperationParameter annotations are unnecessarily declared in
  the @⁠ManagedOperationParameters container.
- The documentation sometimes refers to JmxTestBean when it should
  instead refer to AnnotationTestBean.
- Inconsistent and confusing wording for annotation attributes,
  properties, managed attributes, etc.
- The tables refer to "source-level metadata types/parameters" when
  they should refer to Spring JMX annotations and their attributes.
- The annotation and attribute tables have inconsistent ordering and
  naming for column headers.
- @⁠ManagedNotification and @⁠ManagedMetric are not mentioned.
- The AutodetectCapableMBeanInfoAssembler example is broken since it
  uses the non-annotated JmxTestBean instead of the AnnotationTestBean.

As a side note, the JmxTestBean in our test suite still contains
XDoclet "annotations" which can be safely removed. 😉

Closes gh-33466
This commit is contained in:
Sam Brannen 2024-09-02 18:18:10 +02:00
parent 6e640f0800
commit 1af6480217
9 changed files with 192 additions and 223 deletions

View File

@ -11,10 +11,10 @@ controlling the management interfaces of your beans.
[[jmx-interface-assembler]] [[jmx-interface-assembler]]
== Using the `MBeanInfoAssembler` Interface == Using the `MBeanInfoAssembler` API
Behind the scenes, the `MBeanExporter` delegates to an implementation of the Behind the scenes, the `MBeanExporter` delegates to an implementation of the
`org.springframework.jmx.export.assembler.MBeanInfoAssembler` interface, which is `org.springframework.jmx.export.assembler.MBeanInfoAssembler` API, which is
responsible for defining the management interface of each bean that is exposed. responsible for defining the management interface of each bean that is exposed.
The default implementation, The default implementation,
`org.springframework.jmx.export.assembler.SimpleReflectiveMBeanInfoAssembler`, `org.springframework.jmx.export.assembler.SimpleReflectiveMBeanInfoAssembler`,
@ -28,35 +28,31 @@ or any arbitrary interface.
[[jmx-interface-metadata]] [[jmx-interface-metadata]]
== Using Source-level Metadata: Java Annotations == Using Source-level Metadata: Java Annotations
By using the `MetadataMBeanInfoAssembler`, you can define the management interfaces By using the `MetadataMBeanInfoAssembler`, you can define the management interfaces for
for your beans by using source-level metadata. The reading of metadata is encapsulated your beans by using source-level metadata. The reading of metadata is encapsulated by the
by the `org.springframework.jmx.export.metadata.JmxAttributeSource` interface. `org.springframework.jmx.export.metadata.JmxAttributeSource` interface. Spring JMX
Spring JMX provides a default implementation that uses Java annotations, namely provides a default implementation that uses Java annotations, namely
`org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource`. `org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource`. You must
You must configure the `MetadataMBeanInfoAssembler` with an implementation instance of configure the `MetadataMBeanInfoAssembler` with an implementation instance of the
the `JmxAttributeSource` interface for it to function correctly (there is no default). `JmxAttributeSource` interface for it to function correctly, since there is no default.
To mark a bean for export to JMX, you should annotate the bean class with the To mark a bean for export to JMX, you should annotate the bean class with the
`ManagedResource` annotation. You must mark each method you wish to expose as an operation `@ManagedResource` annotation. You must annotate each method you wish to expose as an
with the `ManagedOperation` annotation and mark each property you wish to expose operation with the `@ManagedOperation` annotation and annotate each property you wish to
with the `ManagedAttribute` annotation. When marking properties, you can omit expose with the `@ManagedAttribute` annotation. When annotating properties, you can omit
either the annotation of the getter or the setter to create a write-only or read-only either the annotation of the getter or the setter to create a write-only or read-only
attribute, respectively. attribute, respectively.
NOTE: A `ManagedResource`-annotated bean must be public, as must the methods exposing NOTE: A `@ManagedResource`-annotated bean must be public, as must the methods exposing
an operation or an attribute. operations or attributes.
The following example shows the annotated version of the `JmxTestBean` class that we The following example shows an annotated version of the `JmxTestBean` class that we
used in xref:integration/jmx/exporting.adoc#jmx-exporting-mbeanserver[Creating an MBeanServer]: used in xref:integration/jmx/exporting.adoc#jmx-exporting-mbeanserver[Creating an MBeanServer].
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
---- ----
package org.springframework.jmx; package org.springframework.jmx;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedAttribute;
@ManagedResource( @ManagedResource(
objectName="bean:name=testBean4", objectName="bean:name=testBean4",
description="My Managed Bean", description="My Managed Bean",
@ -67,20 +63,20 @@ used in xref:integration/jmx/exporting.adoc#jmx-exporting-mbeanserver[Creating a
persistPeriod=200, persistPeriod=200,
persistLocation="foo", persistLocation="foo",
persistName="bar") persistName="bar")
public class AnnotationTestBean implements IJmxTestBean { public class AnnotationTestBean {
private String name;
private int age; private int age;
private String name;
@ManagedAttribute(description="The Age Attribute", currencyTimeLimit=15)
public int getAge() {
return age;
}
public void setAge(int age) { public void setAge(int age) {
this.age = age; this.age = age;
} }
@ManagedAttribute(description="The Age Attribute", currencyTimeLimit=15)
public int getAge() {
return this.age;
}
@ManagedAttribute(description="The Name Attribute", @ManagedAttribute(description="The Name Attribute",
currencyTimeLimit=20, currencyTimeLimit=20,
defaultValue="bar", defaultValue="bar",
@ -91,13 +87,12 @@ used in xref:integration/jmx/exporting.adoc#jmx-exporting-mbeanserver[Creating a
@ManagedAttribute(defaultValue="foo", persistPeriod=300) @ManagedAttribute(defaultValue="foo", persistPeriod=300)
public String getName() { public String getName() {
return name; return this.name;
} }
@ManagedOperation(description="Add two numbers") @ManagedOperation(description="Add two numbers")
@ManagedOperationParameters({ @ManagedOperationParameter(name = "x", description = "The first number")
@ManagedOperationParameter(name = "x", description = "The first number"), @ManagedOperationParameter(name = "y", description = "The second number")
@ManagedOperationParameter(name = "y", description = "The second number")})
public int add(int x, int y) { public int add(int x, int y) {
return x + y; return x + y;
} }
@ -109,36 +104,37 @@ used in xref:integration/jmx/exporting.adoc#jmx-exporting-mbeanserver[Creating a
} }
---- ----
In the preceding example, you can see that the `JmxTestBean` class is marked with the In the preceding example, you can see that the `AnnotationTestBean` class is annotated
`ManagedResource` annotation and that this `ManagedResource` annotation is configured with `@ManagedResource` and that this `@ManagedResource` annotation is configured
with a set of properties. These properties can be used to configure various aspects with a set of attributes. These attributes can be used to configure various aspects
of the MBean that is generated by the `MBeanExporter` and are explained in greater of the MBean that is generated by the `MBeanExporter` and are explained in greater
detail later in xref:integration/jmx/interface.adoc#jmx-interface-metadata-types[Source-level Metadata Types]. detail later in xref:integration/jmx/interface.adoc#jmx-interface-metadata-types[Spring JMX Annotations].
Both the `age` and `name` properties are annotated with the `ManagedAttribute` Both the `age` and `name` properties are annotated with `@ManagedAttribute`,
annotation, but, in the case of the `age` property, only the getter is marked. but, in the case of the `age` property, only the getter method is annotated.
This causes both of these properties to be included in the management interface This causes both of these properties to be included in the management interface
as attributes, but the `age` attribute is read-only. as managed attributes, but the `age` attribute is read-only.
Finally, the `add(int, int)` method is marked with the `ManagedOperation` attribute, Finally, the `add(int, int)` method is annotated with `@ManagedOperation`,
whereas the `dontExposeMe()` method is not. This causes the management interface to whereas the `dontExposeMe()` method is not. This causes the management interface to
contain only one operation (`add(int, int)`) when you use the `MetadataMBeanInfoAssembler`. contain only one operation (`add(int, int)`) when you use the `MetadataMBeanInfoAssembler`.
NOTE: The `AnnotationTestBean` class is not required to implement any Java interfaces,
since the JMX management interface is derived solely from annotations.
The following configuration shows how you can configure the `MBeanExporter` to use the The following configuration shows how you can configure the `MBeanExporter` to use the
`MetadataMBeanInfoAssembler`: `MetadataMBeanInfoAssembler`:
[source,xml,indent=0,subs="verbatim,quotes"] [source,xml,indent=0,subs="verbatim,quotes"]
---- ----
<beans> <beans>
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="assembler" ref="assembler"/> <property name="assembler" ref="assembler"/>
<property name="namingStrategy" ref="namingStrategy"/> <property name="namingStrategy" ref="namingStrategy"/>
<property name="autodetect" value="true"/> <property name="autodetect" value="true"/>
</bean> </bean>
<bean id="jmxAttributeSource"
class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>
<!-- will create management interface using annotation metadata --> <!-- will create management interface using annotation metadata -->
<bean id="assembler" <bean id="assembler"
class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler"> class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
@ -151,102 +147,116 @@ The following configuration shows how you can configure the `MBeanExporter` to u
<property name="attributeSource" ref="jmxAttributeSource"/> <property name="attributeSource" ref="jmxAttributeSource"/>
</bean> </bean>
<bean id="jmxAttributeSource"
class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>
<bean id="testBean" class="org.springframework.jmx.AnnotationTestBean"> <bean id="testBean" class="org.springframework.jmx.AnnotationTestBean">
<property name="name" value="TEST"/> <property name="name" value="TEST"/>
<property name="age" value="100"/> <property name="age" value="100"/>
</bean> </bean>
</beans> </beans>
---- ----
In the preceding example, an `MetadataMBeanInfoAssembler` bean has been configured with an In the preceding example, a `MetadataMBeanInfoAssembler` bean has been configured with an
instance of the `AnnotationJmxAttributeSource` class and passed to the `MBeanExporter` instance of the `AnnotationJmxAttributeSource` class and passed to the `MBeanExporter`
through the assembler property. This is all that is required to take advantage of through the assembler property. This is all that is required to take advantage of
metadata-driven management interfaces for your Spring-exposed MBeans. annotation-driven management interfaces for your Spring-exposed MBeans.
[[jmx-interface-metadata-types]] [[jmx-interface-metadata-types]]
== Source-level Metadata Types == Spring JMX Annotations
The following table describes the source-level metadata types that are available for use in Spring JMX: The following table describes the annotations that are available for use in Spring JMX:
[[jmx-metadata-types]] [[jmx-metadata-types]]
.Source-level metadata types .Spring JMX annotations
[cols="1,1,3"]
|=== |===
| Purpose| Annotation| Annotation Type | Annotation | Applies to | Description
| Mark all instances of a `Class` as JMX managed resources.
| `@ManagedResource` | `@ManagedResource`
| Class | Classes
| Marks all instances of a `Class` as JMX managed resources.
| Mark a method as a JMX operation. | `@ManagedNotification`
| `@ManagedOperation` | Classes
| Method | Indicates a JMX notification emitted by a managed resource.
| Mark a getter or setter as one half of a JMX attribute.
| `@ManagedAttribute` | `@ManagedAttribute`
| Method (only getters and setters) | Methods (only getters and setters)
| Marks a getter or setter as one half of a JMX attribute.
| Define descriptions for operation parameters. | `@ManagedMetric`
| `@ManagedOperationParameter` and `@ManagedOperationParameters` | Methods (only getters)
| Method | Marks a getter as a JMX attribute, with added descriptor properties to indicate that it is a metric.
| `@ManagedOperation`
| Methods
| Marks a method as a JMX operation.
| `@ManagedOperationParameter`
| Methods
| Defines a description for an operation parameter.
|=== |===
The following table describes the configuration parameters that are available for use on these source-level The following table describes some of the common attributes that are available for use in
metadata types: these annotations. Consult the Javadoc for each annotation for further details.
[[jmx-metadata-parameters]] [[jmx-metadata-parameters]]
.Source-level metadata parameters .Spring JMX annotation attributes
[cols="1,3,1"] [cols="1,1,3"]
|=== |===
| Parameter | Description | Applies to | Attribute | Applies to | Description
| `ObjectName` | `objectName`
| `@ManagedResource`
| Used by `MetadataNamingStrategy` to determine the `ObjectName` of a managed resource. | Used by `MetadataNamingStrategy` to determine the `ObjectName` of a managed resource.
| `ManagedResource`
| `description` | `description`
| Sets the friendly description of the resource, attribute or operation. | `@ManagedResource`, `@ManagedNotification`, `@ManagedAttribute`, `@ManagedMetric`,
| `ManagedResource`, `ManagedAttribute`, `ManagedOperation`, or `ManagedOperationParameter` `@ManagedOperation`, `@ManagedOperationParameter`
| Sets the description of the resource, notification, attribute, metric, or operation.
| `currencyTimeLimit` | `currencyTimeLimit`
| `@ManagedResource`, `@ManagedAttribute`, `@ManagedMetric`
| Sets the value of the `currencyTimeLimit` descriptor field. | Sets the value of the `currencyTimeLimit` descriptor field.
| `ManagedResource` or `ManagedAttribute`
| `defaultValue` | `defaultValue`
| `@ManagedAttribute`
| Sets the value of the `defaultValue` descriptor field. | Sets the value of the `defaultValue` descriptor field.
| `ManagedAttribute`
| `log` | `log`
| `@ManagedResource`
| Sets the value of the `log` descriptor field. | Sets the value of the `log` descriptor field.
| `ManagedResource`
| `logFile` | `logFile`
| `@ManagedResource`
| Sets the value of the `logFile` descriptor field. | Sets the value of the `logFile` descriptor field.
| `ManagedResource`
| `persistPolicy` | `persistPolicy`
| `@ManagedResource`, `@ManagedMetric`
| Sets the value of the `persistPolicy` descriptor field. | Sets the value of the `persistPolicy` descriptor field.
| `ManagedResource`
| `persistPeriod` | `persistPeriod`
| `@ManagedResource`, `@ManagedMetric`
| Sets the value of the `persistPeriod` descriptor field. | Sets the value of the `persistPeriod` descriptor field.
| `ManagedResource`
| `persistLocation` | `persistLocation`
| `@ManagedResource`
| Sets the value of the `persistLocation` descriptor field. | Sets the value of the `persistLocation` descriptor field.
| `ManagedResource`
| `persistName` | `persistName`
| `@ManagedResource`
| Sets the value of the `persistName` descriptor field. | Sets the value of the `persistName` descriptor field.
| `ManagedResource`
| `name` | `name`
| `@ManagedOperationParameter`
| Sets the display name of an operation parameter. | Sets the display name of an operation parameter.
| `ManagedOperationParameter`
| `index` | `index`
| `@ManagedOperationParameter`
| Sets the index of an operation parameter. | Sets the index of an operation parameter.
| `ManagedOperationParameter`
|=== |===
@ -255,14 +265,14 @@ metadata types:
To simplify configuration even further, Spring includes the To simplify configuration even further, Spring includes the
`AutodetectCapableMBeanInfoAssembler` interface, which extends the `MBeanInfoAssembler` `AutodetectCapableMBeanInfoAssembler` interface, which extends the `MBeanInfoAssembler`
interface to add support for autodetection of MBean resources. If you configure the interface to add support for auto-detection of MBean resources. If you configure the
`MBeanExporter` with an instance of `AutodetectCapableMBeanInfoAssembler`, it is `MBeanExporter` with an instance of `AutodetectCapableMBeanInfoAssembler`, it is
allowed to "`vote`" on the inclusion of beans for exposure to JMX. allowed to "vote" on the inclusion of beans for exposure to JMX.
The only implementation of the `AutodetectCapableMBeanInfo` interface is The only implementation of the `AutodetectCapableMBeanInfo` interface is
the `MetadataMBeanInfoAssembler`, which votes to include any bean that is marked the `MetadataMBeanInfoAssembler`, which votes to include any bean that is marked
with the `ManagedResource` attribute. The default approach in this case is to use the with the `ManagedResource` attribute. The default approach in this case is to use the
bean name as the `ObjectName`, which results in a configuration similar to the following: bean name as the `ObjectName`, which results in configuration similar to the following:
[source,xml,indent=0,subs="verbatim,quotes"] [source,xml,indent=0,subs="verbatim,quotes"]
---- ----
@ -274,26 +284,29 @@ bean name as the `ObjectName`, which results in a configuration similar to the f
<property name="assembler" ref="assembler"/> <property name="assembler" ref="assembler"/>
</bean> </bean>
<bean id="testBean" class="org.springframework.jmx.JmxTestBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>
<bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler"> <bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
<property name="attributeSource"> <property name="attributeSource">
<bean class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/> <bean class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>
</property> </property>
</bean> </bean>
<bean id="testBean" class="org.springframework.jmx.AnnotationTestBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>
</beans> </beans>
---- ----
Notice that, in the preceding configuration, no beans are passed to the `MBeanExporter`. Notice that, in the preceding configuration, no beans are passed to the `MBeanExporter`.
However, the `JmxTestBean` is still registered, since it is marked with the `ManagedResource` However, the `AnnotationTestBean` is still registered, since it is annotated with
attribute and the `MetadataMBeanInfoAssembler` detects this and votes to include it. `@ManagedResource` and the `MetadataMBeanInfoAssembler` detects this and votes to include
The only problem with this approach is that the name of the `JmxTestBean` now has business it. The only downside with this approach is that the name of the `AnnotationTestBean` now
meaning. You can address this issue by changing the default behavior for `ObjectName` has business meaning. You can address this issue by configuring an `ObjectNamingStrategy`
creation as defined in xref:integration/jmx/naming.adoc[Controlling `ObjectName` Instances for Your Beans]. as explained in xref:integration/jmx/naming.adoc[Controlling `ObjectName` Instances for
Your Beans]. You can also see an example which uses the `MetadataNamingStrategy` in
xref:integration/jmx/interface.adoc#jmx-interface-metadata[Using Source-level Metadata: Java Annotations].
[[jmx-interface-java]] [[jmx-interface-java]]

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2017 the original author or authors. * Copyright 2002-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,19 +20,15 @@ package org.springframework.jmx;
* @author Rob Harrop * @author Rob Harrop
* @author Juergen Hoeller * @author Juergen Hoeller
*/ */
public interface IJmxTestBean { public interface IJmxTestBean extends ITestBean {
int add(int x, int y); int add(int x, int y);
long myOperation(); long myOperation();
int getAge();
void setAge(int age); void setAge(int age);
void setName(String name) throws Exception; int getAge();
String getName();
// used to test invalid methods that exist in the proxy interface // used to test invalid methods that exist in the proxy interface
void dontExposeMe(); void dontExposeMe();

View File

@ -0,0 +1,25 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jmx;
public interface ITestBean {
void setName(String name) throws Exception;
String getName();
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2016 the original author or authors. * Copyright 2002-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,12 +19,6 @@ package org.springframework.jmx;
import java.io.IOException; import java.io.IOException;
/** /**
* @@org.springframework.jmx.export.metadata.ManagedResource
* (description="My Managed Bean", objectName="spring:bean=test",
* log=true, logFile="build/jmx.log", currencyTimeLimit=15, persistPolicy="OnUpdate",
* persistPeriod=200, persistLocation="./foo", persistName="bar.jmx")
* @@org.springframework.jmx.export.metadata.ManagedNotification
* (name="My Notification", description="A Notification", notificationType="type.foo,type.bar")
* @author Rob Harrop * @author Rob Harrop
* @author Juergen Hoeller * @author Juergen Hoeller
*/ */
@ -39,33 +33,21 @@ public class JmxTestBean implements IJmxTestBean {
private boolean isSuperman; private boolean isSuperman;
/** @Override
* @@org.springframework.jmx.export.metadata.ManagedAttribute public void setAge(int age) {
* (description="The Age Attribute", currencyTimeLimit=15) this.age = age;
*/ }
@Override @Override
public int getAge() { public int getAge() {
return age; return age;
} }
@Override
public void setAge(int age) {
this.age = age;
}
/**
* @@org.springframework.jmx.export.metadata.ManagedOperation(currencyTimeLimit=30)
*/
@Override @Override
public long myOperation() { public long myOperation() {
return 1L; return 1L;
} }
/**
* @@org.springframework.jmx.export.metadata.ManagedAttribute
* (description="The Name Attribute", currencyTimeLimit=20,
* defaultValue="bar", persistPolicy="OnUpdate")
*/
@Override @Override
public void setName(String name) throws Exception { public void setName(String name) throws Exception {
if ("Juergen".equals(name)) { if ("Juergen".equals(name)) {
@ -80,20 +62,11 @@ public class JmxTestBean implements IJmxTestBean {
this.name = name; this.name = name;
} }
/**
* @@org.springframework.jmx.export.metadata.ManagedAttribute
* (defaultValue="foo", persistPeriod=300)
*/
@Override @Override
public String getName() { public String getName() {
return name; return name;
} }
/**
* @@org.springframework.jmx.export.metadata.ManagedAttribute(description="The Nick
* Name
* Attribute")
*/
public void setNickName(String nickName) { public void setNickName(String nickName) {
this.nickName = nickName; this.nickName = nickName;
} }
@ -106,30 +79,15 @@ public class JmxTestBean implements IJmxTestBean {
this.isSuperman = superman; this.isSuperman = superman;
} }
/**
* @@org.springframework.jmx.export.metadata.ManagedAttribute(description="The Is
* Superman
* Attribute")
*/
public boolean isSuperman() { public boolean isSuperman() {
return isSuperman; return isSuperman;
} }
/**
* @@org.springframework.jmx.export.metadata.ManagedOperation(description="Add Two
* Numbers
* Together")
* @@org.springframework.jmx.export.metadata.ManagedOperationParameter(index=0, name="x", description="Left operand")
* @@org.springframework.jmx.export.metadata.ManagedOperationParameter(index=1, name="y", description="Right operand")
*/
@Override @Override
public int add(int x, int y) { public int add(int x, int y) {
return x + y; return x + y;
} }
/**
* Test method that is not exposed by the MetadataAssembler.
*/
@Override @Override
public void dontExposeMe() { public void dontExposeMe() {
throw new RuntimeException(); throw new RuntimeException();

View File

@ -22,7 +22,7 @@ import javax.management.modelmbean.ModelMBeanOperationInfo;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.jmx.IJmxTestBean; import org.springframework.jmx.ITestBean;
import org.springframework.jmx.export.assembler.AbstractMetadataAssemblerTests; import org.springframework.jmx.export.assembler.AbstractMetadataAssemblerTests;
import org.springframework.jmx.export.metadata.JmxAttributeSource; import org.springframework.jmx.export.metadata.JmxAttributeSource;
@ -93,7 +93,7 @@ class AnnotationMetadataAssemblerTests extends AbstractMetadataAssemblerTests {
} }
@Override @Override
protected IJmxTestBean createJmxTestBean() { protected ITestBean createJmxTestBean() {
return new AnnotationTestSubBean(); return new AnnotationTestSubBean();
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,7 +16,7 @@
package org.springframework.jmx.export.annotation; package org.springframework.jmx.export.annotation;
import org.springframework.jmx.IJmxTestBean; import org.springframework.jmx.ITestBean;
import org.springframework.jmx.support.MetricType; import org.springframework.jmx.support.MetricType;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -29,7 +29,7 @@ import org.springframework.stereotype.Service;
logFile = "build/jmx.log", currencyTimeLimit = 15, persistPolicy = "OnUpdate", persistPeriod = 200, logFile = "build/jmx.log", currencyTimeLimit = 15, persistPolicy = "OnUpdate", persistPeriod = 200,
persistLocation = "./foo", persistName = "bar.jmx") persistLocation = "./foo", persistName = "bar.jmx")
@ManagedNotification(name = "My Notification", notificationTypes = { "type.foo", "type.bar" }) @ManagedNotification(name = "My Notification", notificationTypes = { "type.foo", "type.bar" })
public class AnnotationTestBean implements IJmxTestBean { public class AnnotationTestBean implements ITestBean {
private String name; private String name;
@ -40,21 +40,13 @@ public class AnnotationTestBean implements IJmxTestBean {
private boolean isSuperman; private boolean isSuperman;
@Override
@ManagedAttribute(description = "The Age Attribute", currencyTimeLimit = 15)
public int getAge() {
return age;
}
@Override
public void setAge(int age) { public void setAge(int age) {
this.age = age; this.age = age;
} }
@Override @ManagedAttribute(description = "The Age Attribute", currencyTimeLimit = 15)
@ManagedOperation(currencyTimeLimit = 30) public int getAge() {
public long myOperation() { return this.age;
return 1L;
} }
@Override @Override
@ -69,7 +61,7 @@ public class AnnotationTestBean implements IJmxTestBean {
@Override @Override
@ManagedAttribute(defaultValue = "foo", persistPeriod = 300) @ManagedAttribute(defaultValue = "foo", persistPeriod = 300)
public String getName() { public String getName() {
return name; return this.name;
} }
@ManagedAttribute(description = "The Nick Name Attribute") @ManagedAttribute(description = "The Nick Name Attribute")
@ -90,7 +82,11 @@ public class AnnotationTestBean implements IJmxTestBean {
return isSuperman; return isSuperman;
} }
@Override @ManagedOperation(currencyTimeLimit = 30)
public long myOperation() {
return 1L;
}
@ManagedOperation(description = "Add Two Numbers Together") @ManagedOperation(description = "Add Two Numbers Together")
@ManagedOperationParameter(name="x", description="Left operand") @ManagedOperationParameter(name="x", description="Left operand")
@ManagedOperationParameter(name="y", description="Right operand") @ManagedOperationParameter(name="y", description="Right operand")
@ -99,9 +95,8 @@ public class AnnotationTestBean implements IJmxTestBean {
} }
/** /**
* Test method that is not exposed by the MetadataAssembler. * Method that is not exposed by the MetadataAssembler.
*/ */
@Override
public void dontExposeMe() { public void dontExposeMe() {
throw new RuntimeException(); throw new RuntimeException();
} }

View File

@ -17,7 +17,6 @@
package org.springframework.jmx.export.annotation; package org.springframework.jmx.export.annotation;
import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.FactoryBean;
import org.springframework.jmx.IJmxTestBean;
/** /**
* @author Juergen Hoeller * @author Juergen Hoeller
@ -36,7 +35,7 @@ public class AnnotationTestBeanFactory implements FactoryBean<FactoryCreatedAnno
} }
@Override @Override
public Class<? extends IJmxTestBean> getObjectType() { public Class<? extends AnnotationTestBean> getObjectType() {
return FactoryCreatedAnnotationTestBean.class; return FactoryCreatedAnnotationTestBean.class;
} }

View File

@ -18,10 +18,8 @@ package org.springframework.jmx.export.assembler;
import javax.management.Attribute; import javax.management.Attribute;
import javax.management.Descriptor; import javax.management.Descriptor;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo; import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo; import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.ObjectInstance; import javax.management.ObjectInstance;
import javax.management.ObjectName; import javax.management.ObjectName;
import javax.management.modelmbean.ModelMBeanAttributeInfo; import javax.management.modelmbean.ModelMBeanAttributeInfo;
@ -31,7 +29,7 @@ import javax.management.modelmbean.ModelMBeanOperationInfo;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.jmx.AbstractJmxTests; import org.springframework.jmx.AbstractJmxTests;
import org.springframework.jmx.IJmxTestBean; import org.springframework.jmx.ITestBean;
import org.springframework.jmx.support.ObjectNameManager; import org.springframework.jmx.support.ObjectNameManager;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -48,76 +46,68 @@ public abstract class AbstractJmxAssemblerTests extends AbstractJmxTests {
protected abstract String getObjectName(); protected abstract String getObjectName();
@Test @Test
void testMBeanRegistration() throws Exception { void mBeanRegistration() throws Exception {
// beans are registered at this point - just grab them from the server // beans are registered at this point - just grab them from the server
ObjectInstance instance = getObjectInstance(); ObjectInstance instance = getObjectInstance();
assertThat(instance).as("Bean should not be null").isNotNull(); assertThat(instance).as("Bean should not be null").isNotNull();
} }
@Test @Test
void testRegisterOperations() throws Exception { void registerOperations() throws Exception {
IJmxTestBean bean = getBean(); assertThat(getBean()).isNotNull();
assertThat(bean).isNotNull();
MBeanInfo inf = getMBeanInfo(); MBeanInfo inf = getMBeanInfo();
assertThat(inf.getOperations()).as("Incorrect number of operations registered").hasSize(getExpectedOperationCount()); assertThat(inf.getOperations()).as("Incorrect number of operations registered").hasSize(getExpectedOperationCount());
} }
@Test @Test
void testRegisterAttributes() throws Exception { void registerAttributes() throws Exception {
IJmxTestBean bean = getBean(); assertThat(getBean()).isNotNull();
assertThat(bean).isNotNull();
MBeanInfo inf = getMBeanInfo(); MBeanInfo inf = getMBeanInfo();
assertThat(inf.getAttributes()).as("Incorrect number of attributes registered").hasSize(getExpectedAttributeCount()); assertThat(inf.getAttributes()).as("Incorrect number of attributes registered").hasSize(getExpectedAttributeCount());
} }
@Test @Test
void testGetMBeanInfo() throws Exception { void getMBeanAttributeInfo() throws Exception {
ModelMBeanInfo info = getMBeanInfoFromAssembler(); ModelMBeanInfo info = getMBeanInfoFromAssembler();
assertThat(info).as("MBeanInfo should not be null").isNotNull(); assertThat(info).as("MBeanInfo should not be null").isNotNull();
assertThat(info.getAttributes())
.hasSize(getExpectedAttributeCount())
.allSatisfy(element -> {
assertThat(element).as("MBeanAttributeInfo should not be null").isNotNull();
assertThat(element.getDescription()).as("Description for MBeanAttributeInfo should not be null").isNotNull();
});
} }
@Test @Test
void testGetMBeanAttributeInfo() throws Exception { void getMBeanOperationInfo() throws Exception {
ModelMBeanInfo info = getMBeanInfoFromAssembler(); ModelMBeanInfo info = getMBeanInfoFromAssembler();
MBeanAttributeInfo[] inf = info.getAttributes(); assertThat(info).as("MBeanInfo should not be null").isNotNull();
assertThat(inf).as("Invalid number of Attributes returned").hasSize(getExpectedAttributeCount()); assertThat(info.getOperations())
.hasSize(getExpectedOperationCount())
for (MBeanAttributeInfo element : inf) { .allSatisfy(element -> {
assertThat(element).as("MBeanAttributeInfo should not be null").isNotNull(); assertThat(element).as("MBeanOperationInfo should not be null").isNotNull();
assertThat(element.getDescription()).as("Description for MBeanAttributeInfo should not be null").isNotNull(); assertThat(element.getDescription()).as("Description for MBeanOperationInfo should not be null").isNotNull();
} });
} }
@Test @Test
void testGetMBeanOperationInfo() throws Exception { void descriptionNotNull() throws Exception {
ModelMBeanInfo info = getMBeanInfoFromAssembler();
MBeanOperationInfo[] inf = info.getOperations();
assertThat(inf).as("Invalid number of Operations returned").hasSize(getExpectedOperationCount());
for (MBeanOperationInfo element : inf) {
assertThat(element).as("MBeanOperationInfo should not be null").isNotNull();
assertThat(element.getDescription()).as("Description for MBeanOperationInfo should not be null").isNotNull();
}
}
@Test
void testDescriptionNotNull() throws Exception {
ModelMBeanInfo info = getMBeanInfoFromAssembler(); ModelMBeanInfo info = getMBeanInfoFromAssembler();
assertThat(info.getDescription()).as("The MBean description should not be null").isNotNull(); assertThat(info.getDescription()).as("The MBean description should not be null").isNotNull();
} }
@Test @Test
void testSetAttribute() throws Exception { void setAttribute() throws Exception {
ObjectName objectName = ObjectNameManager.getInstance(getObjectName()); ObjectName objectName = ObjectNameManager.getInstance(getObjectName());
getServer().setAttribute(objectName, new Attribute(NAME_ATTRIBUTE, "Rob Harrop")); getServer().setAttribute(objectName, new Attribute(NAME_ATTRIBUTE, "Rob Harrop"));
IJmxTestBean bean = (IJmxTestBean) getContext().getBean("testBean"); assertThat(getBean().getName()).isEqualTo("Rob Harrop");
assertThat(bean.getName()).isEqualTo("Rob Harrop");
} }
@Test @Test
void testGetAttribute() throws Exception { void getAttribute() throws Exception {
ObjectName objectName = ObjectNameManager.getInstance(getObjectName()); ObjectName objectName = ObjectNameManager.getInstance(getObjectName());
getBean().setName("John Smith"); getBean().setName("John Smith");
Object val = getServer().getAttribute(objectName, NAME_ATTRIBUTE); Object val = getServer().getAttribute(objectName, NAME_ATTRIBUTE);
@ -125,7 +115,7 @@ public abstract class AbstractJmxAssemblerTests extends AbstractJmxTests {
} }
@Test @Test
void testOperationInvocation() throws Exception{ void operationInvocation() throws Exception{
ObjectName objectName = ObjectNameManager.getInstance(getObjectName()); ObjectName objectName = ObjectNameManager.getInstance(getObjectName());
Object result = getServer().invoke(objectName, "add", Object result = getServer().invoke(objectName, "add",
new Object[] {20, 30}, new String[] {"int", "int"}); new Object[] {20, 30}, new String[] {"int", "int"});
@ -133,7 +123,7 @@ public abstract class AbstractJmxAssemblerTests extends AbstractJmxTests {
} }
@Test @Test
void testAttributeInfoHasDescriptors() throws Exception { void attributeInfoHasDescriptors() throws Exception {
ModelMBeanInfo info = getMBeanInfoFromAssembler(); ModelMBeanInfo info = getMBeanInfoFromAssembler();
ModelMBeanAttributeInfo attr = info.getAttribute(NAME_ATTRIBUTE); ModelMBeanAttributeInfo attr = info.getAttribute(NAME_ATTRIBUTE);
@ -145,42 +135,35 @@ public abstract class AbstractJmxAssemblerTests extends AbstractJmxTests {
} }
@Test @Test
void testAttributeHasCorrespondingOperations() throws Exception { void attributeHasCorrespondingOperations() throws Exception {
ModelMBeanInfo info = getMBeanInfoFromAssembler(); ModelMBeanInfo info = getMBeanInfoFromAssembler();
ModelMBeanOperationInfo get = info.getOperation("getName"); ModelMBeanOperationInfo get = info.getOperation("getName");
assertThat(get).as("get operation should not be null").isNotNull(); assertThat(get).as("get operation should not be null").isNotNull();
assertThat(Integer.valueOf(4)).as("get operation should have visibility of four").isEqualTo(get.getDescriptor().getFieldValue("visibility")); assertThat(get.getDescriptor().getFieldValue("visibility")).as("get operation should have visibility of four").isEqualTo(4);
assertThat(get.getDescriptor().getFieldValue("role")).as("get operation should have role \"getter\"").isEqualTo("getter"); assertThat(get.getDescriptor().getFieldValue("role")).as("get operation should have role \"getter\"").isEqualTo("getter");
ModelMBeanOperationInfo set = info.getOperation("setName"); ModelMBeanOperationInfo set = info.getOperation("setName");
assertThat(set).as("set operation should not be null").isNotNull(); assertThat(set).as("set operation should not be null").isNotNull();
assertThat(Integer.valueOf(4)).as("set operation should have visibility of four").isEqualTo(set.getDescriptor().getFieldValue("visibility")); assertThat(set.getDescriptor().getFieldValue("visibility")).as("set operation should have visibility of four").isEqualTo(4);
assertThat(set.getDescriptor().getFieldValue("role")).as("set operation should have role \"setter\"").isEqualTo("setter"); assertThat(set.getDescriptor().getFieldValue("role")).as("set operation should have role \"setter\"").isEqualTo("setter");
} }
@Test @Test
void testNotificationMetadata() throws Exception { void notificationMetadata() throws Exception {
ModelMBeanInfo info = (ModelMBeanInfo) getMBeanInfo(); ModelMBeanInfo info = (ModelMBeanInfo) getMBeanInfo();
MBeanNotificationInfo[] notifications = info.getNotifications(); MBeanNotificationInfo[] notifications = info.getNotifications();
assertThat(notifications).as("Incorrect number of notifications").hasSize(1); assertThat(notifications).as("Incorrect number of notifications").hasSize(1);
assertThat(notifications[0].getName()).as("Incorrect notification name").isEqualTo("My Notification"); assertThat(notifications[0].getName()).as("Incorrect notification name").isEqualTo("My Notification");
assertThat(notifications[0].getNotifTypes()).as("notification types").containsExactly("type.foo", "type.bar");
String[] notifTypes = notifications[0].getNotifTypes();
assertThat(notifTypes).as("Incorrect number of notification types").hasSize(2);
assertThat(notifTypes[0]).as("Notification type.foo not found").isEqualTo("type.foo");
assertThat(notifTypes[1]).as("Notification type.bar not found").isEqualTo("type.bar");
} }
protected ModelMBeanInfo getMBeanInfoFromAssembler() throws Exception { protected ModelMBeanInfo getMBeanInfoFromAssembler() throws Exception {
IJmxTestBean bean = getBean(); return getAssembler().getMBeanInfo(getBean(), getObjectName());
return getAssembler().getMBeanInfo(bean, getObjectName());
} }
protected IJmxTestBean getBean() { protected ITestBean getBean() {
Object bean = getContext().getBean("testBean"); return getContext().getBean("testBean", ITestBean.class);
return (IJmxTestBean) bean;
} }
protected MBeanInfo getMBeanInfo() throws Exception { protected MBeanInfo getMBeanInfo() throws Exception {

View File

@ -30,7 +30,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.testfixture.interceptor.NopInterceptor; import org.springframework.aop.testfixture.interceptor.NopInterceptor;
import org.springframework.jmx.IJmxTestBean; import org.springframework.jmx.ITestBean;
import org.springframework.jmx.JmxTestBean; import org.springframework.jmx.JmxTestBean;
import org.springframework.jmx.export.MBeanExporter; import org.springframework.jmx.export.MBeanExporter;
import org.springframework.jmx.export.metadata.JmxAttributeSource; import org.springframework.jmx.export.metadata.JmxAttributeSource;
@ -156,7 +156,7 @@ public abstract class AbstractMetadataAssemblerTests extends AbstractJmxAssemble
@Test @Test
void testWithCglibProxy() throws Exception { void testWithCglibProxy() throws Exception {
IJmxTestBean tb = createJmxTestBean(); Object tb = createJmxTestBean();
ProxyFactory pf = new ProxyFactory(); ProxyFactory pf = new ProxyFactory();
pf.setTarget(tb); pf.setTarget(tb);
pf.addAdvice(new NopInterceptor()); pf.addAdvice(new NopInterceptor());
@ -230,7 +230,7 @@ public abstract class AbstractMetadataAssemblerTests extends AbstractJmxAssemble
return 9; return 9;
} }
protected IJmxTestBean createJmxTestBean() { protected ITestBean createJmxTestBean() {
return new JmxTestBean(); return new JmxTestBean();
} }