001/*
002 * Copyright 2002-2019 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.jmx.support;
018
019import javax.management.MBeanServer;
020import javax.management.MBeanServerFactory;
021
022import org.apache.commons.logging.Log;
023import org.apache.commons.logging.LogFactory;
024
025import org.springframework.beans.factory.DisposableBean;
026import org.springframework.beans.factory.FactoryBean;
027import org.springframework.beans.factory.InitializingBean;
028import org.springframework.jmx.MBeanServerNotFoundException;
029import org.springframework.lang.Nullable;
030
031/**
032 * {@link FactoryBean} that obtains a {@link javax.management.MBeanServer} reference
033 * through the standard JMX 1.2 {@link javax.management.MBeanServerFactory}
034 * API.
035 *
036 * <p>Exposes the {@code MBeanServer} for bean references.
037 *
038 * <p>By default, {@code MBeanServerFactoryBean} will always create
039 * a new {@code MBeanServer} even if one is already running. To have
040 * the {@code MBeanServerFactoryBean} attempt to locate a running
041 * {@code MBeanServer} first, set the value of the
042 * "locateExistingServerIfPossible" property to "true".
043 *
044 * @author Rob Harrop
045 * @author Juergen Hoeller
046 * @since 1.2
047 * @see #setLocateExistingServerIfPossible
048 * @see #locateMBeanServer
049 * @see javax.management.MBeanServer
050 * @see javax.management.MBeanServerFactory#findMBeanServer
051 * @see javax.management.MBeanServerFactory#createMBeanServer
052 * @see javax.management.MBeanServerFactory#newMBeanServer
053 * @see MBeanServerConnectionFactoryBean
054 * @see ConnectorServerFactoryBean
055 */
056public class MBeanServerFactoryBean implements FactoryBean<MBeanServer>, InitializingBean, DisposableBean {
057
058        protected final Log logger = LogFactory.getLog(getClass());
059
060        private boolean locateExistingServerIfPossible = false;
061
062        @Nullable
063        private String agentId;
064
065        @Nullable
066        private String defaultDomain;
067
068        private boolean registerWithFactory = true;
069
070        @Nullable
071        private MBeanServer server;
072
073        private boolean newlyRegistered = false;
074
075
076        /**
077         * Set whether or not the {@code MBeanServerFactoryBean} should attempt
078         * to locate a running {@code MBeanServer} before creating one.
079         * <p>Default is {@code false}.
080         */
081        public void setLocateExistingServerIfPossible(boolean locateExistingServerIfPossible) {
082                this.locateExistingServerIfPossible = locateExistingServerIfPossible;
083        }
084
085        /**
086         * Set the agent id of the {@code MBeanServer} to locate.
087         * <p>Default is none. If specified, this will result in an
088         * automatic attempt being made to locate the attendant MBeanServer,
089         * and (importantly) if said MBeanServer cannot be located no
090         * attempt will be made to create a new MBeanServer (and an
091         * MBeanServerNotFoundException will be thrown at resolution time).
092         * <p>Specifying the empty String indicates the platform MBeanServer.
093         * @see javax.management.MBeanServerFactory#findMBeanServer(String)
094         */
095        public void setAgentId(String agentId) {
096                this.agentId = agentId;
097        }
098
099        /**
100         * Set the default domain to be used by the {@code MBeanServer},
101         * to be passed to {@code MBeanServerFactory.createMBeanServer()}
102         * or {@code MBeanServerFactory.findMBeanServer()}.
103         * <p>Default is none.
104         * @see javax.management.MBeanServerFactory#createMBeanServer(String)
105         * @see javax.management.MBeanServerFactory#findMBeanServer(String)
106         */
107        public void setDefaultDomain(String defaultDomain) {
108                this.defaultDomain = defaultDomain;
109        }
110
111        /**
112         * Set whether to register the {@code MBeanServer} with the
113         * {@code MBeanServerFactory}, making it available through
114         * {@code MBeanServerFactory.findMBeanServer()}.
115         * <p>Default is {@code true}.
116         * @see javax.management.MBeanServerFactory#createMBeanServer
117         * @see javax.management.MBeanServerFactory#findMBeanServer
118         */
119        public void setRegisterWithFactory(boolean registerWithFactory) {
120                this.registerWithFactory = registerWithFactory;
121        }
122
123
124        /**
125         * Creates the {@code MBeanServer} instance.
126         */
127        @Override
128        public void afterPropertiesSet() throws MBeanServerNotFoundException {
129                // Try to locate existing MBeanServer, if desired.
130                if (this.locateExistingServerIfPossible || this.agentId != null) {
131                        try {
132                                this.server = locateMBeanServer(this.agentId);
133                        }
134                        catch (MBeanServerNotFoundException ex) {
135                                // If agentId was specified, we were only supposed to locate that
136                                // specific MBeanServer; so let's bail if we can't find it.
137                                if (this.agentId != null) {
138                                        throw ex;
139                                }
140                                logger.debug("No existing MBeanServer found - creating new one");
141                        }
142                }
143
144                // Create a new MBeanServer and register it, if desired.
145                if (this.server == null) {
146                        this.server = createMBeanServer(this.defaultDomain, this.registerWithFactory);
147                        this.newlyRegistered = this.registerWithFactory;
148                }
149        }
150
151        /**
152         * Attempt to locate an existing {@code MBeanServer}.
153         * Called if {@code locateExistingServerIfPossible} is set to {@code true}.
154         * <p>The default implementation attempts to find an {@code MBeanServer} using
155         * a standard lookup. Subclasses may override to add additional location logic.
156         * @param agentId the agent identifier of the MBeanServer to retrieve.
157         * If this parameter is {@code null}, all registered MBeanServers are
158         * considered.
159         * @return the {@code MBeanServer} if found
160         * @throws org.springframework.jmx.MBeanServerNotFoundException
161         * if no {@code MBeanServer} could be found
162         * @see #setLocateExistingServerIfPossible
163         * @see JmxUtils#locateMBeanServer(String)
164         * @see javax.management.MBeanServerFactory#findMBeanServer(String)
165         */
166        protected MBeanServer locateMBeanServer(@Nullable String agentId) throws MBeanServerNotFoundException {
167                return JmxUtils.locateMBeanServer(agentId);
168        }
169
170        /**
171         * Create a new {@code MBeanServer} instance and register it with the
172         * {@code MBeanServerFactory}, if desired.
173         * @param defaultDomain the default domain, or {@code null} if none
174         * @param registerWithFactory whether to register the {@code MBeanServer}
175         * with the {@code MBeanServerFactory}
176         * @see javax.management.MBeanServerFactory#createMBeanServer
177         * @see javax.management.MBeanServerFactory#newMBeanServer
178         */
179        protected MBeanServer createMBeanServer(@Nullable String defaultDomain, boolean registerWithFactory) {
180                if (registerWithFactory) {
181                        return MBeanServerFactory.createMBeanServer(defaultDomain);
182                }
183                else {
184                        return MBeanServerFactory.newMBeanServer(defaultDomain);
185                }
186        }
187
188
189        @Override
190        @Nullable
191        public MBeanServer getObject() {
192                return this.server;
193        }
194
195        @Override
196        public Class<? extends MBeanServer> getObjectType() {
197                return (this.server != null ? this.server.getClass() : MBeanServer.class);
198        }
199
200        @Override
201        public boolean isSingleton() {
202                return true;
203        }
204
205
206        /**
207         * Unregisters the {@code MBeanServer} instance, if necessary.
208         */
209        @Override
210        public void destroy() {
211                if (this.newlyRegistered) {
212                        MBeanServerFactory.releaseMBeanServer(this.server);
213                }
214        }
215
216}