001/* 002 * Copyright 2002-2018 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.util.Map; 021import java.util.Properties; 022 023import org.springframework.core.env.PropertiesPropertySource; 024import org.springframework.core.io.DefaultResourceLoader; 025import org.springframework.core.io.Resource; 026import org.springframework.lang.Nullable; 027import org.springframework.util.StringUtils; 028 029/** 030 * Subclass of {@link PropertiesPropertySource} that loads a {@link Properties} object 031 * from a given {@link org.springframework.core.io.Resource} or resource location such as 032 * {@code "classpath:/com/myco/foo.properties"} or {@code "file:/path/to/file.xml"}. 033 * 034 * <p>Both traditional and XML-based properties file formats are supported; however, in 035 * order for XML processing to take effect, the underlying {@code Resource}'s 036 * {@link org.springframework.core.io.Resource#getFilename() getFilename()} method must 037 * return a non-{@code null} value that ends in {@code ".xml"}. 038 * 039 * @author Chris Beams 040 * @author Juergen Hoeller 041 * @since 3.1 042 * @see org.springframework.core.io.Resource 043 * @see org.springframework.core.io.support.EncodedResource 044 */ 045public class ResourcePropertySource extends PropertiesPropertySource { 046 047 /** The original resource name, if different from the given name. */ 048 @Nullable 049 private final String resourceName; 050 051 052 /** 053 * Create a PropertySource having the given name based on Properties 054 * loaded from the given encoded resource. 055 */ 056 public ResourcePropertySource(String name, EncodedResource resource) throws IOException { 057 super(name, PropertiesLoaderUtils.loadProperties(resource)); 058 this.resourceName = getNameForResource(resource.getResource()); 059 } 060 061 /** 062 * Create a PropertySource based on Properties loaded from the given resource. 063 * The name of the PropertySource will be generated based on the 064 * {@link Resource#getDescription() description} of the given resource. 065 */ 066 public ResourcePropertySource(EncodedResource resource) throws IOException { 067 super(getNameForResource(resource.getResource()), PropertiesLoaderUtils.loadProperties(resource)); 068 this.resourceName = null; 069 } 070 071 /** 072 * Create a PropertySource having the given name based on Properties 073 * loaded from the given encoded resource. 074 */ 075 public ResourcePropertySource(String name, Resource resource) throws IOException { 076 super(name, PropertiesLoaderUtils.loadProperties(new EncodedResource(resource))); 077 this.resourceName = getNameForResource(resource); 078 } 079 080 /** 081 * Create a PropertySource based on Properties loaded from the given resource. 082 * The name of the PropertySource will be generated based on the 083 * {@link Resource#getDescription() description} of the given resource. 084 */ 085 public ResourcePropertySource(Resource resource) throws IOException { 086 super(getNameForResource(resource), PropertiesLoaderUtils.loadProperties(new EncodedResource(resource))); 087 this.resourceName = null; 088 } 089 090 /** 091 * Create a PropertySource having the given name based on Properties loaded from 092 * the given resource location and using the given class loader to load the 093 * resource (assuming it is prefixed with {@code classpath:}). 094 */ 095 public ResourcePropertySource(String name, String location, ClassLoader classLoader) throws IOException { 096 this(name, new DefaultResourceLoader(classLoader).getResource(location)); 097 } 098 099 /** 100 * Create a PropertySource based on Properties loaded from the given resource 101 * location and use the given class loader to load the resource, assuming it is 102 * prefixed with {@code classpath:}. The name of the PropertySource will be 103 * generated based on the {@link Resource#getDescription() description} of the 104 * resource. 105 */ 106 public ResourcePropertySource(String location, ClassLoader classLoader) throws IOException { 107 this(new DefaultResourceLoader(classLoader).getResource(location)); 108 } 109 110 /** 111 * Create a PropertySource having the given name based on Properties loaded from 112 * the given resource location. The default thread context class loader will be 113 * used to load the resource (assuming the location string is prefixed with 114 * {@code classpath:}. 115 */ 116 public ResourcePropertySource(String name, String location) throws IOException { 117 this(name, new DefaultResourceLoader().getResource(location)); 118 } 119 120 /** 121 * Create a PropertySource based on Properties loaded from the given resource 122 * location. The name of the PropertySource will be generated based on the 123 * {@link Resource#getDescription() description} of the resource. 124 */ 125 public ResourcePropertySource(String location) throws IOException { 126 this(new DefaultResourceLoader().getResource(location)); 127 } 128 129 private ResourcePropertySource(String name, @Nullable String resourceName, Map<String, Object> source) { 130 super(name, source); 131 this.resourceName = resourceName; 132 } 133 134 135 /** 136 * Return a potentially adapted variant of this {@link ResourcePropertySource}, 137 * overriding the previously given (or derived) name with the specified name. 138 * @since 4.0.4 139 */ 140 public ResourcePropertySource withName(String name) { 141 if (this.name.equals(name)) { 142 return this; 143 } 144 // Store the original resource name if necessary... 145 if (this.resourceName != null) { 146 if (this.resourceName.equals(name)) { 147 return new ResourcePropertySource(this.resourceName, null, this.source); 148 } 149 else { 150 return new ResourcePropertySource(name, this.resourceName, this.source); 151 } 152 } 153 else { 154 // Current name is resource name -> preserve it in the extra field... 155 return new ResourcePropertySource(name, this.name, this.source); 156 } 157 } 158 159 /** 160 * Return a potentially adapted variant of this {@link ResourcePropertySource}, 161 * overriding the previously given name (if any) with the original resource name 162 * (equivalent to the name generated by the name-less constructor variants). 163 * @since 4.1 164 */ 165 public ResourcePropertySource withResourceName() { 166 if (this.resourceName == null) { 167 return this; 168 } 169 return new ResourcePropertySource(this.resourceName, null, this.source); 170 } 171 172 173 /** 174 * Return the description for the given Resource; if the description is 175 * empty, return the class name of the resource plus its identity hash code. 176 * @see org.springframework.core.io.Resource#getDescription() 177 */ 178 private static String getNameForResource(Resource resource) { 179 String name = resource.getDescription(); 180 if (!StringUtils.hasText(name)) { 181 name = resource.getClass().getSimpleName() + "@" + System.identityHashCode(resource); 182 } 183 return name; 184 } 185 186}