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.FileNotFoundException; 020import java.io.IOException; 021import java.net.SocketException; 022import java.net.UnknownHostException; 023import java.util.Properties; 024 025import org.apache.commons.logging.Log; 026import org.apache.commons.logging.LogFactory; 027 028import org.springframework.core.io.Resource; 029import org.springframework.lang.Nullable; 030import org.springframework.util.CollectionUtils; 031import org.springframework.util.DefaultPropertiesPersister; 032import org.springframework.util.PropertiesPersister; 033 034/** 035 * Base class for JavaBean-style components that need to load properties 036 * from one or more resources. Supports local properties as well, with 037 * configurable overriding. 038 * 039 * @author Juergen Hoeller 040 * @since 1.2.2 041 */ 042public abstract class PropertiesLoaderSupport { 043 044 /** Logger available to subclasses. */ 045 protected final Log logger = LogFactory.getLog(getClass()); 046 047 @Nullable 048 protected Properties[] localProperties; 049 050 protected boolean localOverride = false; 051 052 @Nullable 053 private Resource[] locations; 054 055 private boolean ignoreResourceNotFound = false; 056 057 @Nullable 058 private String fileEncoding; 059 060 private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister(); 061 062 063 /** 064 * Set local properties, e.g. via the "props" tag in XML bean definitions. 065 * These can be considered defaults, to be overridden by properties 066 * loaded from files. 067 */ 068 public void setProperties(Properties properties) { 069 this.localProperties = new Properties[] {properties}; 070 } 071 072 /** 073 * Set local properties, e.g. via the "props" tag in XML bean definitions, 074 * allowing for merging multiple properties sets into one. 075 */ 076 public void setPropertiesArray(Properties... propertiesArray) { 077 this.localProperties = propertiesArray; 078 } 079 080 /** 081 * Set a location of a properties file to be loaded. 082 * <p>Can point to a classic properties file or to an XML file 083 * that follows JDK 1.5's properties XML format. 084 */ 085 public void setLocation(Resource location) { 086 this.locations = new Resource[] {location}; 087 } 088 089 /** 090 * Set locations of properties files to be loaded. 091 * <p>Can point to classic properties files or to XML files 092 * that follow JDK 1.5's properties XML format. 093 * <p>Note: Properties defined in later files will override 094 * properties defined earlier files, in case of overlapping keys. 095 * Hence, make sure that the most specific files are the last 096 * ones in the given list of locations. 097 */ 098 public void setLocations(Resource... locations) { 099 this.locations = locations; 100 } 101 102 /** 103 * Set whether local properties override properties from files. 104 * <p>Default is "false": Properties from files override local defaults. 105 * Can be switched to "true" to let local properties override defaults 106 * from files. 107 */ 108 public void setLocalOverride(boolean localOverride) { 109 this.localOverride = localOverride; 110 } 111 112 /** 113 * Set if failure to find the property resource should be ignored. 114 * <p>"true" is appropriate if the properties file is completely optional. 115 * Default is "false". 116 */ 117 public void setIgnoreResourceNotFound(boolean ignoreResourceNotFound) { 118 this.ignoreResourceNotFound = ignoreResourceNotFound; 119 } 120 121 /** 122 * Set the encoding to use for parsing properties files. 123 * <p>Default is none, using the {@code java.util.Properties} 124 * default encoding. 125 * <p>Only applies to classic properties files, not to XML files. 126 * @see org.springframework.util.PropertiesPersister#load 127 */ 128 public void setFileEncoding(String encoding) { 129 this.fileEncoding = encoding; 130 } 131 132 /** 133 * Set the PropertiesPersister to use for parsing properties files. 134 * The default is DefaultPropertiesPersister. 135 * @see org.springframework.util.DefaultPropertiesPersister 136 */ 137 public void setPropertiesPersister(@Nullable PropertiesPersister propertiesPersister) { 138 this.propertiesPersister = 139 (propertiesPersister != null ? propertiesPersister : new DefaultPropertiesPersister()); 140 } 141 142 143 /** 144 * Return a merged Properties instance containing both the 145 * loaded properties and properties set on this FactoryBean. 146 */ 147 protected Properties mergeProperties() throws IOException { 148 Properties result = new Properties(); 149 150 if (this.localOverride) { 151 // Load properties from file upfront, to let local properties override. 152 loadProperties(result); 153 } 154 155 if (this.localProperties != null) { 156 for (Properties localProp : this.localProperties) { 157 CollectionUtils.mergePropertiesIntoMap(localProp, result); 158 } 159 } 160 161 if (!this.localOverride) { 162 // Load properties from file afterwards, to let those properties override. 163 loadProperties(result); 164 } 165 166 return result; 167 } 168 169 /** 170 * Load properties into the given instance. 171 * @param props the Properties instance to load into 172 * @throws IOException in case of I/O errors 173 * @see #setLocations 174 */ 175 protected void loadProperties(Properties props) throws IOException { 176 if (this.locations != null) { 177 for (Resource location : this.locations) { 178 if (logger.isTraceEnabled()) { 179 logger.trace("Loading properties file from " + location); 180 } 181 try { 182 PropertiesLoaderUtils.fillProperties( 183 props, new EncodedResource(location, this.fileEncoding), this.propertiesPersister); 184 } 185 catch (FileNotFoundException | UnknownHostException | SocketException ex) { 186 if (this.ignoreResourceNotFound) { 187 if (logger.isDebugEnabled()) { 188 logger.debug("Properties resource not found: " + ex.getMessage()); 189 } 190 } 191 else { 192 throw ex; 193 } 194 } 195 } 196 } 197 } 198 199}