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.util.Hashtable;
020
021import javax.management.MalformedObjectNameException;
022import javax.management.ObjectName;
023
024import org.springframework.aop.support.AopUtils;
025import org.springframework.beans.factory.InitializingBean;
026import org.springframework.jmx.export.metadata.JmxAttributeSource;
027import org.springframework.jmx.export.metadata.ManagedResource;
028import org.springframework.jmx.support.ObjectNameManager;
029import org.springframework.lang.Nullable;
030import org.springframework.util.Assert;
031import org.springframework.util.ClassUtils;
032import org.springframework.util.StringUtils;
033
034/**
035 * An implementation of the {@link ObjectNamingStrategy} interface
036 * that reads the {@code ObjectName} from the source-level metadata.
037 * Falls back to the bean key (bean name) if no {@code ObjectName}
038 * can be found in source-level metadata.
039 *
040 * <p>Uses the {@link JmxAttributeSource} strategy interface, so that
041 * metadata can be read using any supported implementation. Out of the box,
042 * {@link org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource}
043 * introspects a well-defined set of Java 5 annotations that come with Spring.
044 *
045 * @author Rob Harrop
046 * @author Juergen Hoeller
047 * @since 1.2
048 * @see ObjectNamingStrategy
049 * @see org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource
050 */
051public class MetadataNamingStrategy implements ObjectNamingStrategy, InitializingBean {
052
053        /**
054         * The {@code JmxAttributeSource} implementation to use for reading metadata.
055         */
056        @Nullable
057        private JmxAttributeSource attributeSource;
058
059        @Nullable
060        private String defaultDomain;
061
062
063        /**
064         * Create a new {@code MetadataNamingStrategy} which needs to be
065         * configured through the {@link #setAttributeSource} method.
066         */
067        public MetadataNamingStrategy() {
068        }
069
070        /**
071         * Create a new {@code MetadataNamingStrategy} for the given
072         * {@code JmxAttributeSource}.
073         * @param attributeSource the JmxAttributeSource to use
074         */
075        public MetadataNamingStrategy(JmxAttributeSource attributeSource) {
076                Assert.notNull(attributeSource, "JmxAttributeSource must not be null");
077                this.attributeSource = attributeSource;
078        }
079
080
081        /**
082         * Set the implementation of the {@code JmxAttributeSource} interface to use
083         * when reading the source-level metadata.
084         */
085        public void setAttributeSource(JmxAttributeSource attributeSource) {
086                Assert.notNull(attributeSource, "JmxAttributeSource must not be null");
087                this.attributeSource = attributeSource;
088        }
089
090        /**
091         * Specify the default domain to be used for generating ObjectNames
092         * when no source-level metadata has been specified.
093         * <p>The default is to use the domain specified in the bean name
094         * (if the bean name follows the JMX ObjectName syntax); else,
095         * the package name of the managed bean class.
096         */
097        public void setDefaultDomain(String defaultDomain) {
098                this.defaultDomain = defaultDomain;
099        }
100
101        @Override
102        public void afterPropertiesSet() {
103                if (this.attributeSource == null) {
104                        throw new IllegalArgumentException("Property 'attributeSource' is required");
105                }
106        }
107
108
109        /**
110         * Reads the {@code ObjectName} from the source-level metadata associated
111         * with the managed resource's {@code Class}.
112         */
113        @Override
114        public ObjectName getObjectName(Object managedBean, @Nullable String beanKey) throws MalformedObjectNameException {
115                Assert.state(this.attributeSource != null, "No JmxAttributeSource set");
116                Class<?> managedClass = AopUtils.getTargetClass(managedBean);
117                ManagedResource mr = this.attributeSource.getManagedResource(managedClass);
118
119                // Check that an object name has been specified.
120                if (mr != null && StringUtils.hasText(mr.getObjectName())) {
121                        return ObjectNameManager.getInstance(mr.getObjectName());
122                }
123                else {
124                        Assert.state(beanKey != null, "No ManagedResource attribute and no bean key specified");
125                        try {
126                                return ObjectNameManager.getInstance(beanKey);
127                        }
128                        catch (MalformedObjectNameException ex) {
129                                String domain = this.defaultDomain;
130                                if (domain == null) {
131                                        domain = ClassUtils.getPackageName(managedClass);
132                                }
133                                Hashtable<String, String> properties = new Hashtable<>();
134                                properties.put("type", ClassUtils.getShortName(managedClass));
135                                properties.put("name", beanKey);
136                                return ObjectNameManager.getInstance(domain, properties);
137                        }
138                }
139        }
140
141}