001package org.javasimon.jmx;
002
003import org.javasimon.Manager;
004import org.javasimon.SimonException;
005import org.javasimon.SimonManager;
006import org.slf4j.Logger;
007import org.slf4j.LoggerFactory;
008
009import java.lang.management.ManagementFactory;
010
011import javax.management.JMException;
012import javax.management.MBeanServer;
013import javax.management.ObjectName;
014
015/**
016 * Utility class for registering JMX beans that can be used to access Simon manager and Simons.
017 * JmxReporter is created by either {@link org.javasimon.jmx.JmxReporter#forManager(org.javasimon.Manager)}
018 * or {@link JmxReporter#forDefaultManager()}. Additional configuration methods can be called using fluent
019 * API ().
020 * Finally {@link JmxReporter#start()} will set up all expected MX beans and system is ready for monitoring
021 * through JMX. Method {@link JmxReporter#stop()} unregisters all related MX beans when monitoring is no
022 * longer required.
023 *
024 * @author <a href="mailto:ivan.mushketyk@gmail.com">Ivan Mushketyk</a>
025 * @author <a href="mailto:virgo47@gmail.com">Richard "Virgo" Richter</a>
026 */
027public final class JmxReporter {
028
029        public static final String DEFAULT_BEAN_NAME = "org.javasimon.jmx.JmxReporter:type=Simon";
030        public static final String DEFAULT_SIMON_DOMAIN = "org.javasimon.jmx.simons";
031
032        private static final Logger logger = LoggerFactory.getLogger(JmxReporter.class);
033
034        /** Simon manager that will be accessed through registered JMX bean */
035        private final Manager manager;
036
037        /** JMX bean server used by JmxReporter */
038        private MBeanServer beanServer;
039
040        /** Name of a new JMX bean for Simon manager */
041        private String beanName;
042
043        /** Should already registered bean with the specified name be replaced. */
044        private boolean replaceExistingMxBeans;
045
046        /** Should JMX beans for separate Simons be registered. */
047        private boolean registerSimons;
048
049        /**
050         * Should JMX beans for already existing Simons be registered ({@link #registerSimons} must be true
051         * for this to have effect).
052         */
053        private boolean registerExistingSimons;
054
055        /** Domain of JMX beans for separate Simons. */
056        private String simonDomain;
057
058        private JmxRegisterCallback jmxRegisterCallback;
059
060        private JmxReporter(Manager manager) {
061                if (manager == null) {
062                        throw new IllegalArgumentException("Manager is null");
063                }
064
065                this.manager = manager;
066        }
067
068        /**
069         * Creates new JmxReporter for the specified manager with default values.
070         *
071         * @param manager manager that will be used by JmxReporter
072         * @return new JmxReporter
073         */
074        static public JmxReporter forManager(Manager manager) {
075                JmxReporter reporter = new JmxReporter(manager);
076
077                reporter.setBeanName(DEFAULT_BEAN_NAME);
078                reporter.simonDomain = DEFAULT_SIMON_DOMAIN;
079                reporter.beanServer = ManagementFactory.getPlatformMBeanServer();
080
081                return reporter;
082        }
083
084        /**
085         * Creates new JmxReporter for the default manager with default values.
086         *
087         * @return new JmxReporter
088         */
089        public static JmxReporter forDefaultManager() {
090                return forManager(SimonManager.manager());
091        }
092
093
094        /**
095         * Specify domain for registered JMX for separate Simons. Is used only if {@link #registerSimons} set to true.
096         *
097         * @param simonDomain domain for separate JMX beans for separate Simons
098         * @return this
099         */
100        public JmxReporter simonDomain(String simonDomain) {
101                setSimonDomain(simonDomain);
102                return this;
103        }
104
105        /**
106         * Specifies that previously registered bean with the same name should be replaced.
107         *
108         * @return this
109         */
110        public JmxReporter replaceExisting() {
111                replaceExistingMxBeans = true;
112                return this;
113        }
114
115        /**
116         * Specifies that separate JMX beans should be registered for each Simon in current manager.
117         *
118         * @return this
119         */
120        public JmxReporter registerSimons() {
121                registerSimons = true;
122                return this;
123        }
124
125        public JmxReporter registerExistingSimons() {
126                this.registerExistingSimons = true;
127                return this;
128        }
129
130        /**
131         * Bean name that will be used to register JMX bean for Simon manager.
132         *
133         * @param beanName bean name that will be used to register JMX bean for Simon manager
134         * @return this
135         */
136        public JmxReporter beanName(String beanName) {
137                setBeanName(beanName);
138                return this;
139        }
140
141        JmxReporter beanServer(MBeanServer beanServer) {
142                setBeanServer(beanServer);
143                return this;
144        }
145
146        private void setBeanName(String beanName) {
147                if (beanName == null || beanName.isEmpty()) {
148                        throw new IllegalArgumentException("Bean name is null or empty");
149                }
150
151                this.beanName = beanName;
152        }
153
154        public Manager getManager() {
155                return manager;
156        }
157
158        public MBeanServer getBeanServer() {
159                return beanServer;
160        }
161
162        public void setBeanServer(MBeanServer beanServer) {
163                this.beanServer = beanServer;
164        }
165
166        public String getBeanName() {
167                return beanName;
168        }
169
170        public String getSimonDomain() {
171                return simonDomain;
172        }
173
174        public void setSimonDomain(String simonDomain) {
175                if (simonDomain == null || simonDomain.isEmpty()) {
176                        throw new IllegalArgumentException("Simon domain is null or empty");
177                }
178                this.simonDomain = simonDomain;
179        }
180
181        public boolean isReplaceExistingMxBeans() {
182                return replaceExistingMxBeans;
183        }
184
185        public void setReplaceExistingMxBeans(boolean replaceExistingMxBeans) {
186                this.replaceExistingMxBeans = replaceExistingMxBeans;
187        }
188
189        public boolean isRegisterSimons() {
190                return registerSimons;
191        }
192
193        public void setRegisterSimons(boolean registerSimons) {
194                this.registerSimons = registerSimons;
195        }
196
197        public boolean isRegisterExistingSimons() {
198                return registerExistingSimons;
199        }
200
201        public void setRegisterExistingSimons(boolean registerExistingSimons) {
202                this.registerExistingSimons = registerExistingSimons;
203        }
204
205        /** Starts JmxReporter - registers all required beans in JMX bean server. */
206        public JmxReporter start() {
207                SimonManagerMXBean simonManagerMXBean = new SimonManagerMXBeanImpl(manager);
208                registerMXBean(simonManagerMXBean, beanName);
209
210                if (registerSimons) {
211                        jmxRegisterCallback = new JmxRegisterCallback(beanServer, simonDomain);
212                        if (registerExistingSimons) {
213                                jmxRegisterCallback.setRegisterExisting(true);
214                        }
215                        manager.callback().addCallback(jmxRegisterCallback);
216                }
217                return this;
218        }
219
220        private void registerMXBean(Object bean, String newBeanName) {
221                try {
222                        ObjectName beanObjectName = new ObjectName(newBeanName);
223                        if (replaceExistingMxBeans && beanServer.isRegistered(beanObjectName)) {
224                                logger.warn("Replacing existing SimonManager JMX bean");
225                                beanServer.unregisterMBean(beanObjectName);
226                        }
227
228                        logger.info("Registering new SimonManager JMX bean with name {}", newBeanName);
229                        beanServer.registerMBean(bean, beanObjectName);
230                } catch (JMException e) {
231                        throw new SimonException("Failed to register Jmx reporter", e);
232                }
233        }
234
235        /** Stop JMX reporter. Unregister all previously registered beans. */
236        public void stop() {
237                unregisterManagerBean();
238                unregisterSimonBeans();
239        }
240
241        private void unregisterManagerBean() {
242                try {
243                        ObjectName beanObjectName = new ObjectName(beanName);
244                        if (beanServer.isRegistered(beanObjectName)) {
245                                beanServer.unregisterMBean(beanObjectName);
246                        } else {
247                                logger.warn("SimonManager JMX bean with name {} was not registered", beanName);
248                        }
249                } catch (JMException e) {
250                        throw new SimonException("Failed to unregister SimonManager MX bean", e);
251                }
252        }
253
254        private void unregisterSimonBeans() {
255                if (jmxRegisterCallback != null) {
256                        manager.callback().removeCallback(jmxRegisterCallback);
257                }
258        }
259}