001/*
002 * Copyright 2002-2019 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.Properties;
020
021import org.springframework.beans.factory.FactoryBean;
022import org.springframework.beans.factory.InitializingBean;
023import org.springframework.core.CollectionFactory;
024import org.springframework.lang.Nullable;
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 * <p>Requires SnakeYAML 1.18 or higher, as of Spring Framework 5.0.6.
078 *
079 * @author Dave Syer
080 * @author Stephane Nicoll
081 * @author Juergen Hoeller
082 * @since 4.1
083 */
084public class YamlPropertiesFactoryBean extends YamlProcessor implements FactoryBean<Properties>, InitializingBean {
085
086        private boolean singleton = true;
087
088        @Nullable
089        private Properties properties;
090
091
092        /**
093         * Set if a singleton should be created, or a new object on each request
094         * otherwise. Default is {@code true} (a singleton).
095         */
096        public void setSingleton(boolean singleton) {
097                this.singleton = singleton;
098        }
099
100        @Override
101        public boolean isSingleton() {
102                return this.singleton;
103        }
104
105        @Override
106        public void afterPropertiesSet() {
107                if (isSingleton()) {
108                        this.properties = createProperties();
109                }
110        }
111
112        @Override
113        @Nullable
114        public Properties getObject() {
115                return (this.properties != null ? this.properties : createProperties());
116        }
117
118        @Override
119        public Class<?> getObjectType() {
120                return Properties.class;
121        }
122
123
124        /**
125         * Template method that subclasses may override to construct the object
126         * returned by this factory. The default implementation returns a
127         * properties with the content of all resources.
128         * <p>Invoked lazily the first time {@link #getObject()} is invoked in
129         * case of a shared singleton; else, on each {@link #getObject()} call.
130         * @return the object returned by this factory
131         * @see #process(MatchCallback)
132         */
133        protected Properties createProperties() {
134                Properties result = CollectionFactory.createStringAdaptingProperties();
135                process((properties, map) -> result.putAll(properties));
136                return result;
137        }
138
139}