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