001/*
002 * Copyright 2011 Atteo.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 */
014package org.atteo.moonshine.jmx;
015
016import java.lang.management.ManagementFactory;
017import java.util.ArrayList;
018import java.util.List;
019
020import javax.inject.Inject;
021import javax.inject.Singleton;
022import javax.management.InstanceAlreadyExistsException;
023import javax.management.InstanceNotFoundException;
024import javax.management.MBeanRegistrationException;
025import javax.management.MBeanServer;
026import javax.management.MalformedObjectNameException;
027import javax.management.NotCompliantMBeanException;
028import javax.management.ObjectInstance;
029import javax.management.ObjectName;
030import javax.xml.bind.annotation.XmlRootElement;
031
032import org.atteo.classindex.ClassIndex;
033import org.atteo.moonshine.TopLevelService;
034
035import com.google.inject.AbstractModule;
036import com.google.inject.Injector;
037import com.google.inject.Module;
038
039/**
040 * Jmx helper service.
041 *
042 * <p>
043 * Starts MBean server and automatically registers any class annotated with &#064;{@link MBean}.
044 * </p>
045 */
046@XmlRootElement(name = "jmx")
047public class Jmx extends TopLevelService {
048    @Override
049    public Module configure() {
050        return new AbstractModule() {
051            @Override
052            protected void configure() {
053                bind(MBeanServer.class).toInstance(ManagementFactory.getPlatformMBeanServer());
054
055                for (Class<?> klass : ClassIndex.getAnnotated(MBean.class)) {
056                    bind(klass).in(Singleton.class);
057                }
058            }
059        };
060    }
061
062    @Inject
063    private Injector injector;
064
065    @Inject
066    private MBeanServer server;
067
068    private List<ObjectName> registeredNames = new ArrayList<>();
069
070    @Override
071    public void start() {
072        try {
073            for (Class<?> klass : ClassIndex.getAnnotated(MBean.class)) {
074                Object instance = injector.getInstance(klass);
075                ObjectName name;
076                MBean annotation = klass.getAnnotation(MBean.class);
077                if (annotation != null && !"".equals(annotation.name())) {
078                    name = ObjectName.getInstance(annotation.name());
079                } else {
080                    name = ObjectName.getInstance(klass.getPackage().getName() + ":type="
081                            + klass.getSimpleName());
082                }
083                ObjectInstance mbeanInstance = server.registerMBean(instance, name);
084                registeredNames.add(mbeanInstance.getObjectName());
085            }
086        } catch (MalformedObjectNameException | InstanceAlreadyExistsException | MBeanRegistrationException
087                | NotCompliantMBeanException e) {
088            throw new RuntimeException(e);
089        }
090    }
091
092    @Override
093    public void close() {
094        for (ObjectName name : registeredNames) {
095            try {
096                server.unregisterMBean(name);
097            } catch (InstanceNotFoundException e) {
098                // not found? ignore
099                continue;
100            } catch (MBeanRegistrationException e) {
101                throw new RuntimeException(e);
102            }
103        }
104    }
105}