从JBoss 4.x到JBoss 7的端口MBean
我们目前正在将一些项目从JBoss 4.x移植到JBoss 7.到目前为止,除了我们通常用于提供简单管理操作的MBean之外,一切似乎都运行正常。
我一直在寻找相当长的一段时间,但要么我无法提出正确的搜索术语,要么我缺少一些知识来弥补JBoss 4.x和JBoss 7中的MBean定义之间的差距。
因此,希望有人可以提供我可能缺少的内容或我必须阅读的内容(可能是一些文档,示例等)
在Jboss 4.x中,我们的MBean通常如下所示:
@Service( objectName = "Domain:Name=SomeMBean", xmbean="resource:") class SomeMBean { @EJB private SomeService someService; public String someOperation() { someService.doSomething(); return "success"; } }
我们使用@Service
注释来定义对象名称和xmbean描述符,JBoss会自动注册那些mbeans。
显然,在JBoss 7中, @Service
注释不再存在,因此需要另一种方法。
到目前为止,我设法用平台mbean服务器手动注册MBean,但我更喜欢JBoss自动执行此操作。 另外,到目前为止,我还没有设法提供方法/参数的描述(尽管这些function更加出色)。
为了清楚起见,我会重复这个问题:
如何在JBoss 7(Java EE 6)中定义提供以下function的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()
方法,并且将在MBean服务器中注册该bean。
以下是启动过程的概述:
- 自定义CDI扩展扫描每个CDI bean以获取自定义@MBean批注
- 对于每个可忽略的MBean,注入目标被包裹
- 将启动一个单独的bean,它在@PostConstruct方法中将创建MBean的实例
- 将
postConstruct()
MBean注入目标的postConstruct()
方法,因此MBean在MBean服务器中注册
此方法的一个缺点是在执行MBean方法时缺少事务上下文(任何EJB调用都将在事务上下文中运行)。 但是,如果需要,可以使用CDI拦截器来修复,这将提供事务上下文。 Seam项目似乎有适当的拦截器。
我仍然不确定这是否是一种理智而稳定的方法,所以任何建设性的评论,提示等都非常受欢迎。
使用@Startup的Singleton EJB? http://www.adam-bien.com/roller/abien/entry/singleton_the_simplest_possible_jmx
我认为更简洁的方法是使用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 void processInjectionTarget(@Observes ProcessInjectionTarget pit) { // check if the MBean annotation is present AnnotatedType 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 delegate = pit.getInjectionTarget(); InjectionTarget wrapper = new JmxInjectionTarget (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 ObjectName getObjectName(AnnotatedType 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 implements InjectionTarget { private final InjectionTarget d; private final ObjectName objectName; public JmxInjectionTarget(InjectionTarget delegate, ObjectName objectName) { this.d = delegate; this.objectName = objectName; } @Override public void dispose(T instance) { d.dispose(instance); } @Override public Set getInjectionPoints() { return d.getInjectionPoints(); } @Override public T produce(CreationalContext ctx) { return d.produce(ctx); } @Override public void inject(T instance, CreationalContext 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
奇迹般有效 )