001/*
002 * Copyright 2002-2020 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.core.io.support;
018
019import java.io.IOException;
020import java.io.InputStream;
021import java.io.Reader;
022import java.net.URL;
023import java.net.URLConnection;
024import java.util.Enumeration;
025import java.util.Properties;
026
027import org.springframework.core.io.Resource;
028import org.springframework.lang.Nullable;
029import org.springframework.util.Assert;
030import org.springframework.util.ClassUtils;
031import org.springframework.util.DefaultPropertiesPersister;
032import org.springframework.util.PropertiesPersister;
033import org.springframework.util.ResourceUtils;
034
035/**
036 * Convenient utility methods for loading of {@code java.util.Properties},
037 * performing standard handling of input streams.
038 *
039 * <p>For more configurable properties loading, including the option of a
040 * customized encoding, consider using the PropertiesLoaderSupport class.
041 *
042 * @author Juergen Hoeller
043 * @author Rob Harrop
044 * @since 2.0
045 * @see PropertiesLoaderSupport
046 */
047public abstract class PropertiesLoaderUtils {
048
049        private static final String XML_FILE_EXTENSION = ".xml";
050
051
052        /**
053         * Load properties from the given EncodedResource,
054         * potentially defining a specific encoding for the properties file.
055         * @see #fillProperties(java.util.Properties, EncodedResource)
056         */
057        public static Properties loadProperties(EncodedResource resource) throws IOException {
058                Properties props = new Properties();
059                fillProperties(props, resource);
060                return props;
061        }
062
063        /**
064         * Fill the given properties from the given EncodedResource,
065         * potentially defining a specific encoding for the properties file.
066         * @param props the Properties instance to load into
067         * @param resource the resource to load from
068         * @throws IOException in case of I/O errors
069         */
070        public static void fillProperties(Properties props, EncodedResource resource)
071                        throws IOException {
072
073                fillProperties(props, resource, new DefaultPropertiesPersister());
074        }
075
076        /**
077         * Actually load properties from the given EncodedResource into the given Properties instance.
078         * @param props the Properties instance to load into
079         * @param resource the resource to load from
080         * @param persister the PropertiesPersister to use
081         * @throws IOException in case of I/O errors
082         */
083        static void fillProperties(Properties props, EncodedResource resource, PropertiesPersister persister)
084                        throws IOException {
085
086                InputStream stream = null;
087                Reader reader = null;
088                try {
089                        String filename = resource.getResource().getFilename();
090                        if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
091                                stream = resource.getInputStream();
092                                persister.loadFromXml(props, stream);
093                        }
094                        else if (resource.requiresReader()) {
095                                reader = resource.getReader();
096                                persister.load(props, reader);
097                        }
098                        else {
099                                stream = resource.getInputStream();
100                                persister.load(props, stream);
101                        }
102                }
103                finally {
104                        if (stream != null) {
105                                stream.close();
106                        }
107                        if (reader != null) {
108                                reader.close();
109                        }
110                }
111        }
112
113        /**
114         * Load properties from the given resource (in ISO-8859-1 encoding).
115         * @param resource the resource to load from
116         * @return the populated Properties instance
117         * @throws IOException if loading failed
118         * @see #fillProperties(java.util.Properties, Resource)
119         */
120        public static Properties loadProperties(Resource resource) throws IOException {
121                Properties props = new Properties();
122                fillProperties(props, resource);
123                return props;
124        }
125
126        /**
127         * Fill the given properties from the given resource (in ISO-8859-1 encoding).
128         * @param props the Properties instance to fill
129         * @param resource the resource to load from
130         * @throws IOException if loading failed
131         */
132        public static void fillProperties(Properties props, Resource resource) throws IOException {
133                try (InputStream is = resource.getInputStream()) {
134                        String filename = resource.getFilename();
135                        if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
136                                props.loadFromXML(is);
137                        }
138                        else {
139                                props.load(is);
140                        }
141                }
142        }
143
144        /**
145         * Load all properties from the specified class path resource
146         * (in ISO-8859-1 encoding), using the default class loader.
147         * <p>Merges properties if more than one resource of the same name
148         * found in the class path.
149         * @param resourceName the name of the class path resource
150         * @return the populated Properties instance
151         * @throws IOException if loading failed
152         */
153        public static Properties loadAllProperties(String resourceName) throws IOException {
154                return loadAllProperties(resourceName, null);
155        }
156
157        /**
158         * Load all properties from the specified class path resource
159         * (in ISO-8859-1 encoding), using the given class loader.
160         * <p>Merges properties if more than one resource of the same name
161         * found in the class path.
162         * @param resourceName the name of the class path resource
163         * @param classLoader the ClassLoader to use for loading
164         * (or {@code null} to use the default class loader)
165         * @return the populated Properties instance
166         * @throws IOException if loading failed
167         */
168        public static Properties loadAllProperties(String resourceName, @Nullable ClassLoader classLoader) throws IOException {
169                Assert.notNull(resourceName, "Resource name must not be null");
170                ClassLoader classLoaderToUse = classLoader;
171                if (classLoaderToUse == null) {
172                        classLoaderToUse = ClassUtils.getDefaultClassLoader();
173                }
174                Enumeration<URL> urls = (classLoaderToUse != null ? classLoaderToUse.getResources(resourceName) :
175                                ClassLoader.getSystemResources(resourceName));
176                Properties props = new Properties();
177                while (urls.hasMoreElements()) {
178                        URL url = urls.nextElement();
179                        URLConnection con = url.openConnection();
180                        ResourceUtils.useCachesIfNecessary(con);
181                        try (InputStream is = con.getInputStream()) {
182                                if (resourceName.endsWith(XML_FILE_EXTENSION)) {
183                                        props.loadFromXML(is);
184                                }
185                                else {
186                                        props.load(is);
187                                }
188                        }
189                }
190                return props;
191        }
192
193}