001/*
002 * Copyright 2002-2018 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.export.naming;
018
019import java.io.IOException;
020import java.util.Properties;
021
022import javax.management.MalformedObjectNameException;
023import javax.management.ObjectName;
024
025import org.apache.commons.logging.Log;
026import org.apache.commons.logging.LogFactory;
027
028import org.springframework.beans.factory.InitializingBean;
029import org.springframework.core.io.Resource;
030import org.springframework.core.io.support.PropertiesLoaderUtils;
031import org.springframework.jmx.support.ObjectNameManager;
032import org.springframework.lang.Nullable;
033import org.springframework.util.Assert;
034import org.springframework.util.CollectionUtils;
035
036/**
037 * {@code ObjectNamingStrategy} implementation that builds
038 * {@code ObjectName} instances from the key used in the
039 * "beans" map passed to {@code MBeanExporter}.
040 *
041 * <p>Can also check object name mappings, given as {@code Properties}
042 * or as {@code mappingLocations} of properties files. The key used
043 * to look up is the key used in {@code MBeanExporter}'s "beans" map.
044 * If no mapping is found for a given key, the key itself is used to
045 * build an {@code ObjectName}.
046 *
047 * @author Rob Harrop
048 * @author Juergen Hoeller
049 * @since 1.2
050 * @see #setMappings
051 * @see #setMappingLocation
052 * @see #setMappingLocations
053 * @see org.springframework.jmx.export.MBeanExporter#setBeans
054 */
055public class KeyNamingStrategy implements ObjectNamingStrategy, InitializingBean {
056
057        /**
058         * {@code Log} instance for this class.
059         */
060        protected final Log logger = LogFactory.getLog(getClass());
061
062        /**
063         * Stores the mappings of bean key to {@code ObjectName}.
064         */
065        @Nullable
066        private Properties mappings;
067
068        /**
069         * Stores the {@code Resource}s containing properties that should be loaded
070         * into the final merged set of {@code Properties} used for {@code ObjectName}
071         * resolution.
072         */
073        @Nullable
074        private Resource[] mappingLocations;
075
076        /**
077         * Stores the result of merging the {@code mappings} {@code Properties}
078         * with the properties stored in the resources defined by {@code mappingLocations}.
079         */
080        @Nullable
081        private Properties mergedMappings;
082
083
084        /**
085         * Set local properties, containing object name mappings, e.g. via
086         * the "props" tag in XML bean definitions. These can be considered
087         * defaults, to be overridden by properties loaded from files.
088         */
089        public void setMappings(Properties mappings) {
090                this.mappings = mappings;
091        }
092
093        /**
094         * Set a location of a properties file to be loaded,
095         * containing object name mappings.
096         */
097        public void setMappingLocation(Resource location) {
098                this.mappingLocations = new Resource[] {location};
099        }
100
101        /**
102         * Set location of properties files to be loaded,
103         * containing object name mappings.
104         */
105        public void setMappingLocations(Resource... mappingLocations) {
106                this.mappingLocations = mappingLocations;
107        }
108
109
110        /**
111         * Merges the {@code Properties} configured in the {@code mappings} and
112         * {@code mappingLocations} into the final {@code Properties} instance
113         * used for {@code ObjectName} resolution.
114         */
115        @Override
116        public void afterPropertiesSet() throws IOException {
117                this.mergedMappings = new Properties();
118                CollectionUtils.mergePropertiesIntoMap(this.mappings, this.mergedMappings);
119
120                if (this.mappingLocations != null) {
121                        for (Resource location : this.mappingLocations) {
122                                if (logger.isDebugEnabled()) {
123                                        logger.debug("Loading JMX object name mappings file from " + location);
124                                }
125                                PropertiesLoaderUtils.fillProperties(this.mergedMappings, location);
126                        }
127                }
128        }
129
130
131        /**
132         * Attempts to retrieve the {@code ObjectName} via the given key, trying to
133         * find a mapped value in the mappings first.
134         */
135        @Override
136        public ObjectName getObjectName(Object managedBean, @Nullable String beanKey) throws MalformedObjectNameException {
137                Assert.notNull(beanKey, "KeyNamingStrategy requires bean key");
138                String objectName = null;
139                if (this.mergedMappings != null) {
140                        objectName = this.mergedMappings.getProperty(beanKey);
141                }
142                if (objectName == null) {
143                        objectName = beanKey;
144                }
145                return ObjectNameManager.getInstance(objectName);
146        }
147
148}