从JBoss 4.x到JBoss 7的端口MBean

时间:2012-02-14 10:49:53

标签: java jmx jboss7.x mbeans

我们目前正在将一些项目从JBoss 4.x移植到JBoss 7。 到目前为止,除了我们的MBean之外,一切似乎都运行良好,我们通常用它来提供简单的管理操作。

我现在已经搜索了很长一段时间,但要么我无法使用正确的搜索词,要么我缺少一些知识来弥补JBoss 4.x中MBean定义与JBoss 7。

因此,希望有人可以提供我可能遗失的内容或我必须阅读的内容(可能是一些文档,示例等)。

在Jboss 4.x中,我们的MBean通常如下所示:

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

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

我们使用@Service注释来定义对象名称和xmbean描述符,JBoss会自动注册那些mbeans。

显然,在JBoss 7中,@Service注释不再存在,因此需要另一种方法。

到目前为止,我设法用平台mbean服务器手动注册MBean,但我更喜欢JBoss自动执行此操作。另外,到目前为止,我还没有设法提供方法/参数的描述(尽管那些功能更加出色)。

为了清楚起见,我会重复这个问题:

如何在JBoss 7(Java EE 6)中定义提供以下功能的MBean?

  • 自动部署
  • 访问EJB
  • 可通过JConsole或JMX-Console访问(我目前正在使用Dimitris Andreadis的端口)
  • 提供方法/参数的说明

更新

这是我到目前为止所得到的:

首先,我发现了这个投影,它使用CDI来包装相应注释的任何bean的注入目标,并在postConstruct()方法中进行JMX注册:http://code.google.com/p/jmx-annotations/。此外,扫描找到的MBean以获取类/属性/方法/参数注释,这些注释为注释属性提供描述。

但是,似乎没有为EJB调用postConstruct()方法(我假设这是为了不与EJB容器冲突)。因此MBean现在不应该是EJB而是普通的CDI bean。

然而,这样做的缺点是MBean不会自动实例化。为了解决这个问题,有一个单例bean在启动时循环遍历BeanManager中的所有bean,并创建每个找到的MBean的实例。由于MBean仍然具有其注入目标,因此不会调用其postConstruct()方法,并且该bean将在MBean服务器中注册。

以下是启动程序的概述:

  • 自定义CDI扩展扫描每个CDI bean以获取自定义@MBean注释
  • 为每个可忽略的MBean注入目标
  • 将启动一个单独的bean,它的@PostConstruct方法将创建MBean的实例
  • 将调用MBean注入目标的postConstruct()方法,因此MBean在MBean服务器中注册

此方法的一个缺点是在执行MBean方法时缺少事务上下文(任何EJB调用都将在事务上下文中运行)。但是,如果需要,可以使用CDI拦截器修复这个问题,该拦截器将提供事务上下文。 Seam项目似乎有适当的拦截器。

我仍然不确定这是否是一种理智而稳定的方法,所以任何建设性的评论,提示等都非常受欢迎。

2 个答案:

答案 0 :(得分:3)

答案 1 :(得分:1)

我认为更简洁的方法是使用CDI扩展。 请看一下我们使用的解决方案:

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

...

这是CDI扩展的工作代码:

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);
        }

    }
}

然后只需通过@Mbean注释标记您的类,并将自动在Mbean服务器中注册:

@Startup 
@Singleton 
@MBean("com.synapsense:type=JmxBindName")
public class SomeService

像魅力一样工作)