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