001/*
002 * Copyright 2002-2016 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.beans.factory.config;
018
019import java.util.Map;
020import java.util.Properties;
021
022import org.springframework.beans.factory.FactoryBean;
023import org.springframework.beans.factory.InitializingBean;
024import org.springframework.core.CollectionFactory;
025
026/**
027 * Factory for {@link java.util.Properties} that reads from a YAML source,
028 * exposing a flat structure of String property values.
029 *
030 * <p>YAML is a nice human-readable format for configuration, and it has some
031 * useful hierarchical properties. It's more or less a superset of JSON, so it
032 * has a lot of similar features.
033 *
034 * <p><b>Note: All exposed values are of type {@code String}</b> for access through
035 * the common {@link Properties#getProperty} method (e.g. in configuration property
036 * resolution through {@link PropertyResourceConfigurer#setProperties(Properties)}).
037 * If this is not desirable, use {@link YamlMapFactoryBean} instead.
038 *
039 * <p>The Properties created by this factory have nested paths for hierarchical
040 * objects, so for instance this YAML
041 *
042 * <pre class="code">
043 * environments:
044 *   dev:
045 *     url: https://dev.bar.com
046 *     name: Developer Setup
047 *   prod:
048 *     url: https://foo.bar.com
049 *     name: My Cool App
050 * </pre>
051 *
052 * is transformed into these properties:
053 *
054 * <pre class="code">
055 * environments.dev.url=https://dev.bar.com
056 * environments.dev.name=Developer Setup
057 * environments.prod.url=https://foo.bar.com
058 * environments.prod.name=My Cool App
059 * </pre>
060 *
061 * Lists are split as property keys with <code>[]</code> dereferencers, for
062 * example this YAML:
063 *
064 * <pre class="code">
065 * servers:
066 * - dev.bar.com
067 * - foo.bar.com
068 * </pre>
069 *
070 * becomes properties like this:
071 *
072 * <pre class="code">
073 * servers[0]=dev.bar.com
074 * servers[1]=foo.bar.com
075 * </pre>
076 *
077 * @author Dave Syer
078 * @author Stephane Nicoll
079 * @author Juergen Hoeller
080 * @since 4.1
081 */
082public class YamlPropertiesFactoryBean extends YamlProcessor implements FactoryBean<Properties>, InitializingBean {
083
084        private boolean singleton = true;
085
086        private Properties properties;
087
088
089        /**
090         * Set if a singleton should be created, or a new object on each request
091         * otherwise. Default is {@code true} (a singleton).
092         */
093        public void setSingleton(boolean singleton) {
094                this.singleton = singleton;
095        }
096
097        @Override
098        public boolean isSingleton() {
099                return this.singleton;
100        }
101
102        @Override
103        public void afterPropertiesSet() {
104                if (isSingleton()) {
105                        this.properties = createProperties();
106                }
107        }
108
109        @Override
110        public Properties getObject() {
111                return (this.properties != null ? this.properties : createProperties());
112        }
113
114        @Override
115        public Class<?> getObjectType() {
116                return Properties.class;
117        }
118
119
120        /**
121         * Template method that subclasses may override to construct the object
122         * returned by this factory. The default implementation returns a
123         * properties with the content of all resources.
124         * <p>Invoked lazily the first time {@link #getObject()} is invoked in
125         * case of a shared singleton; else, on each {@link #getObject()} call.
126         * @return the object returned by this factory
127         * @see #process(MatchCallback) ()
128         */
129        protected Properties createProperties() {
130                final Properties result = CollectionFactory.createStringAdaptingProperties();
131                process(new MatchCallback() {
132                        @Override
133                        public void process(Properties properties, Map<String, Object> map) {
134                                result.putAll(properties);
135                        }
136                });
137                return result;
138        }
139
140}