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