Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Port MBean from JBoss 4.x to JBoss 7

we're currently in the process of porting some of our projects from JBoss 4.x to JBoss 7. So far everything seems to work fine, except for our MBeans, which we're commonly using to provide simple management operations.

I've been searching for quite a while now, but either I'm incapable of comming up with the correct search term or I'm missing some piece of knowledge to bridge the gap between MBean definition in JBoss 4.x and JBoss 7.

Thus, hopefully someone can provide a hint on what I might be missing or where I'd have to read on (maybe some documentation, examples etc.)

In Jboss 4.x our MBeans often look like this:

@Service( objectName = "Domain:Name=SomeMBean",
  xmbean="resource:<path-to-xmbean.xml>")
class SomeMBean 
{
  @EJB
  private SomeService someService;    

  public String someOperation()
  {
     someService.doSomething();
     return "success";
  }
}

We used the @Service annotation to define the object name and xmbean descriptor and JBoss would automatically register those mbeans.

Apparently, in JBoss 7 the @Service annotation doesn't exist anymore and thus another approach is needed.

So far, I managed to register the MBean manually with the platform mbean server, but I'd rather like JBoss to do that automatically. Additionally, I didn't manage to provide descriptions for the methods/parameters so far (although those are more of a nice to have feature).

I'll repeat the question for clarity:

How would I define an MBean in JBoss 7 (Java EE 6) that provides the following features?

  • automatic deployment
  • access to EJBs
  • accessible through JConsole or JMX-Console (I'm currently using Dimitris Andreadis' port)
  • provide descriptions for methods/parameters

Update

Here's what I got so far:

First, I found this projection, which uses CDI to wrap the injection target of any bean that is annotated accordingly and does the JMX registration in the postConstruct() method: http://code.google.com/p/jmx-annotations/. Additionally, the found MBeans are scanned for class/attribute/method/parameter annotations that provide a description for the annotated property.

However, the postConstruct() method seems not to be called for EJBs (I assume that is in order not to clash with the EJB container). Thus MBeans now should not be EJBs but plain CDI beans.

Thus, however, has the drawback that MBeans are not automatically instantiated. To overcome this, there is a singleton bean that at startup loops through all beans in the BeanManager and creates an instance of every MBean found. Because the MBeans still have their injection target, its postConstruct() method will no be called and the bean will be registered in the MBean server.

Here's a rough overview of the startup procedure:

  • a custom CDI extension scans each CDI bean for the custom @MBean annotation
  • for each elligible MBean the injection target is wrapped
  • a singleton bean will be started which in its @PostConstruct method will create instances of the MBeans
  • the postConstruct() method of the MBean's injection target will be called and thus the MBean is registered in the MBean server

One drawback of this method would be the missing transaction context when executing MBean methods (any EJB calls will run in a transaction context). Howver, this could be fixed using a CDI interceptor that will provide for the transaction context, if needed. The Seam project seems to have appropriate interceptors for that.

I'm still not sure if this is a sane and stable approach, so any constructive comments, hints etc. are more than welcome.

like image 443
Thomas Avatar asked Feb 14 '12 10:02

Thomas


2 Answers

Singleton EJB with @Startup? http://www.adam-bien.com/roller/abien/entry/singleton_the_simplest_possible_jmx

like image 51
Chase Avatar answered Oct 21 '22 15:10

Chase


I think more concise way to do it is to use CDI extension. Please take a look at the solution we use:

@Documented
@Retention(value=RUNTIME)
@Target(value=TYPE)
@Inherited
public @interface MBean {
    String value() default "";
}

...

This is working code of CDI extension:

public class ManagementExtension implements Extension {

    private static Logger log = LoggerFactory
            .getLogger(ManagementExtension.class);

    public <T> void processInjectionTarget(@Observes ProcessInjectionTarget<T> pit) {

        // check if the MBean annotation is present
        AnnotatedType<T> at = pit.getAnnotatedType();
        if (at.isAnnotationPresent(MBean.class)) {
            // it makes sense to register JMX interfaces only for singletons
            if (!at.isAnnotationPresent(Singleton.class)) {
                log.warn("Ignoring attemt to register JMX interface for a non-singleton EJB: "
                        + at.getJavaClass().getName());
                return;
            }

            try {
                // decorate the InjectionTarget
                InjectionTarget<T> delegate = pit.getInjectionTarget();
                InjectionTarget<T> wrapper = new JmxInjectionTarget<T>(delegate, getObjectName(at));

                // change the InjectionTarget with the decorated one
                pit.setInjectionTarget(wrapper);
            } catch (Exception e) {
                log.warn("Cannot get JMX object name for: " + at.getJavaClass().getName(), e);
            }

        }
    }

    private <T> ObjectName getObjectName(AnnotatedType<T> at) throws MalformedObjectNameException {

        String name = at.getAnnotation(MBean.class).value();

        if (name.isEmpty()) {
            name = at.getJavaClass().getPackage().getName() + ":type="
                    + at.getJavaClass().getSimpleName();
        }

        return new ObjectName(name);
    }

    private class JmxInjectionTarget<T> implements InjectionTarget<T> {

        private final InjectionTarget<T> d;
        private final ObjectName objectName;

        public JmxInjectionTarget(InjectionTarget<T> delegate, ObjectName objectName) {
            this.d = delegate;
            this.objectName = objectName;
        }
        @Override
        public void dispose(T instance) {
            d.dispose(instance);
        }

        @Override
        public Set<InjectionPoint> getInjectionPoints() {
            return d.getInjectionPoints();
        }

        @Override
        public T produce(CreationalContext<T> ctx) {
            return d.produce(ctx);
        }

        @Override
        public void inject(T instance, CreationalContext<T> ctx) {
            d.inject(instance, ctx);
            //the next piece of code better be done in postConstruct but...
            //got no idea why but postConstruct never gets called
            //for Singleton EJB bean
            MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
            try {
                if(mBeanServer.isRegistered(objectName))
                mBeanServer.unregisterMBean(objectName);
                mBeanServer.registerMBean(instance, objectName);
            } catch (Exception e) {
                log.warn("Cannot register "+objectName, e);
                return;
            }
            log.info("added JMX registration for: " + objectName);
        }

        @Override
        public void postConstruct(T instance) {
            d.postConstruct(instance);
        }

        @Override
        public void preDestroy(T instance) {
            d.preDestroy(instance);
        }

    }
}

Then just mark your class by @Mbean annotation and will be automatically registered in Mbean server :

@Startup 
@Singleton 
@MBean("com.company=JmxBindName")
public class SomeService

Works like a charm )

like image 1
Zorg Avatar answered Oct 21 '22 14:10

Zorg