001/*
002 * Copyright 2002-2020 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.context.annotation;
018
019import java.util.Map;
020
021import javax.management.MBeanServer;
022import javax.naming.NamingException;
023
024import org.springframework.beans.factory.BeanFactory;
025import org.springframework.beans.factory.BeanFactoryAware;
026import org.springframework.beans.factory.config.BeanDefinition;
027import org.springframework.context.EnvironmentAware;
028import org.springframework.core.annotation.AnnotationAttributes;
029import org.springframework.core.env.Environment;
030import org.springframework.core.type.AnnotationMetadata;
031import org.springframework.jmx.MBeanServerNotFoundException;
032import org.springframework.jmx.export.annotation.AnnotationMBeanExporter;
033import org.springframework.jmx.support.RegistrationPolicy;
034import org.springframework.jmx.support.WebSphereMBeanServerFactoryBean;
035import org.springframework.jndi.JndiLocatorDelegate;
036import org.springframework.lang.Nullable;
037import org.springframework.util.Assert;
038import org.springframework.util.ClassUtils;
039import org.springframework.util.StringUtils;
040
041/**
042 * {@code @Configuration} class that registers a {@link AnnotationMBeanExporter} bean.
043 *
044 * <p>This configuration class is automatically imported when using the
045 * {@link EnableMBeanExport} annotation. See its javadoc for complete usage details.
046 *
047 * @author Phillip Webb
048 * @author Chris Beams
049 * @since 3.2
050 * @see EnableMBeanExport
051 */
052@Configuration
053@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
054public class MBeanExportConfiguration implements ImportAware, EnvironmentAware, BeanFactoryAware {
055
056        private static final String MBEAN_EXPORTER_BEAN_NAME = "mbeanExporter";
057
058        @Nullable
059        private AnnotationAttributes enableMBeanExport;
060
061        @Nullable
062        private Environment environment;
063
064        @Nullable
065        private BeanFactory beanFactory;
066
067
068        @Override
069        public void setImportMetadata(AnnotationMetadata importMetadata) {
070                Map<String, Object> map = importMetadata.getAnnotationAttributes(EnableMBeanExport.class.getName());
071                this.enableMBeanExport = AnnotationAttributes.fromMap(map);
072                if (this.enableMBeanExport == null) {
073                        throw new IllegalArgumentException(
074                                        "@EnableMBeanExport is not present on importing class " + importMetadata.getClassName());
075                }
076        }
077
078        @Override
079        public void setEnvironment(Environment environment) {
080                this.environment = environment;
081        }
082
083        @Override
084        public void setBeanFactory(BeanFactory beanFactory) {
085                this.beanFactory = beanFactory;
086        }
087
088
089        @Bean(name = MBEAN_EXPORTER_BEAN_NAME)
090        @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
091        public AnnotationMBeanExporter mbeanExporter() {
092                AnnotationMBeanExporter exporter = new AnnotationMBeanExporter();
093                Assert.state(this.enableMBeanExport != null, "No EnableMBeanExport annotation found");
094                setupDomain(exporter, this.enableMBeanExport);
095                setupServer(exporter, this.enableMBeanExport);
096                setupRegistrationPolicy(exporter, this.enableMBeanExport);
097                return exporter;
098        }
099
100        private void setupDomain(AnnotationMBeanExporter exporter, AnnotationAttributes enableMBeanExport) {
101                String defaultDomain = enableMBeanExport.getString("defaultDomain");
102                if (StringUtils.hasLength(defaultDomain) && this.environment != null) {
103                        defaultDomain = this.environment.resolvePlaceholders(defaultDomain);
104                }
105                if (StringUtils.hasText(defaultDomain)) {
106                        exporter.setDefaultDomain(defaultDomain);
107                }
108        }
109
110        private void setupServer(AnnotationMBeanExporter exporter, AnnotationAttributes enableMBeanExport) {
111                String server = enableMBeanExport.getString("server");
112                if (StringUtils.hasLength(server) && this.environment != null) {
113                        server = this.environment.resolvePlaceholders(server);
114                }
115                if (StringUtils.hasText(server)) {
116                        Assert.state(this.beanFactory != null, "No BeanFactory set");
117                        exporter.setServer(this.beanFactory.getBean(server, MBeanServer.class));
118                }
119                else {
120                        SpecificPlatform specificPlatform = SpecificPlatform.get();
121                        if (specificPlatform != null) {
122                                MBeanServer mbeanServer = specificPlatform.getMBeanServer();
123                                if (mbeanServer != null) {
124                                        exporter.setServer(mbeanServer);
125                                }
126                        }
127                }
128        }
129
130        private void setupRegistrationPolicy(AnnotationMBeanExporter exporter, AnnotationAttributes enableMBeanExport) {
131                RegistrationPolicy registrationPolicy = enableMBeanExport.getEnum("registration");
132                exporter.setRegistrationPolicy(registrationPolicy);
133        }
134
135
136        /**
137         * Specific platforms that might need custom MBean handling.
138         */
139        public enum SpecificPlatform {
140
141                /**
142                 * Weblogic.
143                 */
144                WEBLOGIC("weblogic.management.Helper") {
145                        @Override
146                        public MBeanServer getMBeanServer() {
147                                try {
148                                        return new JndiLocatorDelegate().lookup("java:comp/env/jmx/runtime", MBeanServer.class);
149                                }
150                                catch (NamingException ex) {
151                                        throw new MBeanServerNotFoundException("Failed to retrieve WebLogic MBeanServer from JNDI", ex);
152                                }
153                        }
154                },
155
156                /**
157                 * Websphere.
158                 */
159                WEBSPHERE("com.ibm.websphere.management.AdminServiceFactory") {
160                        @Override
161                        public MBeanServer getMBeanServer() {
162                                WebSphereMBeanServerFactoryBean fb = new WebSphereMBeanServerFactoryBean();
163                                fb.afterPropertiesSet();
164                                return fb.getObject();
165                        }
166                };
167
168                private final String identifyingClass;
169
170                SpecificPlatform(String identifyingClass) {
171                        this.identifyingClass = identifyingClass;
172                }
173
174                @Nullable
175                public abstract MBeanServer getMBeanServer();
176
177                @Nullable
178                public static SpecificPlatform get() {
179                        ClassLoader classLoader = MBeanExportConfiguration.class.getClassLoader();
180                        for (SpecificPlatform environment : values()) {
181                                if (ClassUtils.isPresent(environment.identifyingClass, classLoader)) {
182                                        return environment;
183                                }
184                        }
185                        return null;
186                }
187        }
188
189}