Same JMX Mbean class for many application at same server
Asked Answered
T

5

5

I have more than 5 spring web application and all of them are utilizing another common library. This common library has its own MBeans. Because of mandatory unique objectName constraint, my applications could not be deployed on same server.

The way I am using MBeans are like this:

@ManagedResource(objectName = "com.org.city:name=City", description = "City related operations")

I would like to use same MBean class with different objectNames for all applications. What is the correct way to utilize it without duplicating my MBeans.

Thanks

Towrope answered 16/8, 2011 at 7:44 Comment(0)
T
4

I have implemented ObjectNamingStrategy for custom behaviour.

   @Override
  public ObjectName getObjectName(Object managedBean, String beanKey) throws MalformedObjectNameException {
     Class managedClass = AopUtils.getTargetClass(managedBean);
     Hashtable<String, String> properties = new Hashtable<String, String>();
     properties.put("type",ClassUtils.getPackageName(managedClass).concat(".").concat(ClassUtils.getShortName(managedClass)));
     properties.put("name", beanKey);
     return ObjectNameManager.getInstance(domain, properties);
  }
Towrope answered 18/8, 2011 at 8:14 Comment(0)
F
5

I ran into the same issue, and built off of Cemo's solution. Here is a sample implementation.

context.xml

<!-- Set up jmx bean auto scanning -->
<!-- Note: we're not using <context:mbean-export /> because we need to provide our own naming strategy -->
<bean id="mbeanExporter" class="org.springframework.jmx.export.annotation.AnnotationMBeanExporter">
    <property name="namingStrategy">
        <bean class="com.foo.MultiAppMetadataNamingStrategy">
            <property name="applicationName" value="${application.name}" />
        </bean>
    </property>
</bean>

MultiAppMetadataNamingStrategy.java

public class MultiAppMetadataNamingStrategy implements ObjectNamingStrategy, InitializingBean {

    private String applicationName;

    public MultiAppMetadataNamingStrategy() {
    }

    public MultiAppMetadataNamingStrategy(String applicationName) {
        this.applicationName = Preconditions.checkNotNull(applicationName, "applicationName must not be null");
    }

    public void setApplicationName(String applicationName) {
        this.applicationName = Preconditions.checkNotNull(applicationName, "applicationName must not be null");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        if (applicationName == null) {
            throw new IllegalArgumentException("Property 'applicationName' is required");
        }
    }

    @Override
    public ObjectName getObjectName(Object managedBean, String beanKey) throws MalformedObjectNameException {
        Class managedClass = AopUtils.getTargetClass(managedBean);
        String domain = ClassUtils.getPackageName(managedClass);

        Hashtable<String, String> properties = new Hashtable<>();
        properties.put("type", ClassUtils.getShortName(managedClass));
        properties.put("name", beanKey);
        // ensure the application name is included as a property in the object name
        properties.put("app", applicationName);
        return ObjectNameManager.getInstance(domain, properties);
    }
}

This allows setting up an mbean like:

package com.foo;

@ManagedResource(description = "Bean description")
public class MyBean {
    ...
}

which will register an mbean with object name com.foo:name=myBean,type=MyBean,app=CustomAppName

Fictional answered 18/10, 2013 at 16:53 Comment(0)
T
4

I have implemented ObjectNamingStrategy for custom behaviour.

   @Override
  public ObjectName getObjectName(Object managedBean, String beanKey) throws MalformedObjectNameException {
     Class managedClass = AopUtils.getTargetClass(managedBean);
     Hashtable<String, String> properties = new Hashtable<String, String>();
     properties.put("type",ClassUtils.getPackageName(managedClass).concat(".").concat(ClassUtils.getShortName(managedClass)));
     properties.put("name", beanKey);
     return ObjectNameManager.getInstance(domain, properties);
  }
Towrope answered 18/8, 2011 at 8:14 Comment(0)
B
0

You need to change the registering behavoiur of the mbean exporter:

<property name="registrationBehaviorName" value="REGISTRATION_REPLACE_EXISTING"/>

But this will still mean only one application registers the bean. And you can't logically have more than one mbean with the same name from multiple applications. How will be determined which application to invoke? Use the application name as a prefix to the mbean name.

Bleary answered 16/8, 2011 at 7:52 Comment(8)
I would like to use same mbean for all applications. In this case it will be utilized for just last deployed application?Towrope
you can't. Register with a different name (use some version). Otherwise how will it know which application to invoke?Bleary
Yes I have same concerns but I dont know how to register with a different name. Is there any way to parameterized these objectName according to parent application?Towrope
you should be able to use SPeL for the name, together with some externalized configuration property. For example app.name, and the name is ${app.name}SomeManagedBeanBleary
But I am using annoations and they are utilized type level. What is the correct usages of Spel with annotation which are type level?Towrope
well, try using spel in the annotation - it works in many cases. @...(name="${prop}FooBar")Bleary
I started to feel that I am missing something. The expression has not been evaluated. Please note that I am using ManagedResource annotation not Value ..Towrope
well, then it is not supported. You can extend the AnnotationMBeanExporter and provide your own logic, I think.Bleary
D
0

You can define a simple naming strategy based on properties using place holder(s).
Every war will have it;s own copy of the app.properties
E.g.

With a properties file :app.properties

appName=MyApp1 #Every app will have it own value e.g,MyApp2,MyApp3,MyApp4,MyApp5

and a PropertiesPlaceHolder

<bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
     <property name="placeholderPrefix" value="$app{" />    
     <property name="location" value="classpath:app.properties"/>
</bean>

Defining objectName

@ManagedResource(objectName=com.mycompany:name=$app{appName}-MyBean")
public class MyBean {}

Your bean will be named

com.mycompany
    +MyApp1-MyBean
    +MyApp2-MyBean
    +MyApp3-MyBean
    +MyApp4-MyBean
    +MyApp5-MyBean

You can use more the one property place holder.
Works with Spring 4.0.2

Doleful answered 6/4, 2016 at 8:7 Comment(0)
M
0

These answers helped point me in the right direction, but there were a few pieces missing for an Annotation based setup (I'm not using Spring Boot however)

The Spring Docs on this subject say:

If you prefer using the annotation based approach to define your management interfaces, then a convenience subclass of MBeanExporter is available: AnnotationMBeanExporter. When defining an instance of this subclass, the namingStrategy, assembler, and attributeSource configuration is no longer needed, since it will always use standard Java annotation-based metadata (autodetection is always enabled as well). In fact, rather than defining an MBeanExporter bean, an even simpler syntax is supported by the @EnableMBeanExport @Configuration annotation.

But using @EnableMBeanExport prevents you from having the ability of defining your own NamingStrategy

So Instead of just setup a @Bean method that returns my MBeanExporter with a custom naming strategy that uses the context path.

@Configuration
public class JmxUtil {

    @Value("#{servletContext.contextPath}")
    private String contextPath;
    private String domain = "foo.bar";

    @Bean
    public MBeanExporter mbeanExporter() {
        AnnotationMBeanExporter exporter = new AnnotationMBeanExporter();
        exporter.setNamingStrategy((managedBean, beanKey) -> {
            return ObjectNameManager.getInstance(domain, new Hashtable<>(ImmutableMap.of(
                "name", beanKey,
                "instance", contextPath
            )));
        });
        exporter.setDefaultDomain(domain);
        return exporter;
    }
}
Mishap answered 7/12, 2017 at 16:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.