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; 018 019import java.io.FileNotFoundException; 020import java.io.IOException; 021import java.io.InputStream; 022import java.net.URL; 023 024import org.springframework.lang.Nullable; 025import org.springframework.util.Assert; 026import org.springframework.util.ClassUtils; 027import org.springframework.util.ObjectUtils; 028import org.springframework.util.StringUtils; 029 030/** 031 * {@link Resource} implementation for class path resources. Uses either a 032 * given {@link ClassLoader} or a given {@link Class} for loading resources. 033 * 034 * <p>Supports resolution as {@code java.io.File} if the class path 035 * resource resides in the file system, but not for resources in a JAR. 036 * Always supports resolution as URL. 037 * 038 * @author Juergen Hoeller 039 * @author Sam Brannen 040 * @since 28.12.2003 041 * @see ClassLoader#getResourceAsStream(String) 042 * @see Class#getResourceAsStream(String) 043 */ 044public class ClassPathResource extends AbstractFileResolvingResource { 045 046 private final String path; 047 048 @Nullable 049 private ClassLoader classLoader; 050 051 @Nullable 052 private Class<?> clazz; 053 054 055 /** 056 * Create a new {@code ClassPathResource} for {@code ClassLoader} usage. 057 * A leading slash will be removed, as the ClassLoader resource access 058 * methods will not accept it. 059 * <p>The thread context class loader will be used for 060 * loading the resource. 061 * @param path the absolute path within the class path 062 * @see java.lang.ClassLoader#getResourceAsStream(String) 063 * @see org.springframework.util.ClassUtils#getDefaultClassLoader() 064 */ 065 public ClassPathResource(String path) { 066 this(path, (ClassLoader) null); 067 } 068 069 /** 070 * Create a new {@code ClassPathResource} for {@code ClassLoader} usage. 071 * A leading slash will be removed, as the ClassLoader resource access 072 * methods will not accept it. 073 * @param path the absolute path within the classpath 074 * @param classLoader the class loader to load the resource with, 075 * or {@code null} for the thread context class loader 076 * @see ClassLoader#getResourceAsStream(String) 077 */ 078 public ClassPathResource(String path, @Nullable ClassLoader classLoader) { 079 Assert.notNull(path, "Path must not be null"); 080 String pathToUse = StringUtils.cleanPath(path); 081 if (pathToUse.startsWith("/")) { 082 pathToUse = pathToUse.substring(1); 083 } 084 this.path = pathToUse; 085 this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader()); 086 } 087 088 /** 089 * Create a new {@code ClassPathResource} for {@code Class} usage. 090 * The path can be relative to the given class, or absolute within 091 * the classpath via a leading slash. 092 * @param path relative or absolute path within the class path 093 * @param clazz the class to load resources with 094 * @see java.lang.Class#getResourceAsStream 095 */ 096 public ClassPathResource(String path, @Nullable Class<?> clazz) { 097 Assert.notNull(path, "Path must not be null"); 098 this.path = StringUtils.cleanPath(path); 099 this.clazz = clazz; 100 } 101 102 /** 103 * Create a new {@code ClassPathResource} with optional {@code ClassLoader} 104 * and {@code Class}. Only for internal usage. 105 * @param path relative or absolute path within the classpath 106 * @param classLoader the class loader to load the resource with, if any 107 * @param clazz the class to load resources with, if any 108 * @deprecated as of 4.3.13, in favor of selective use of 109 * {@link #ClassPathResource(String, ClassLoader)} vs {@link #ClassPathResource(String, Class)} 110 */ 111 @Deprecated 112 protected ClassPathResource(String path, @Nullable ClassLoader classLoader, @Nullable Class<?> clazz) { 113 this.path = StringUtils.cleanPath(path); 114 this.classLoader = classLoader; 115 this.clazz = clazz; 116 } 117 118 119 /** 120 * Return the path for this resource (as resource path within the class path). 121 */ 122 public final String getPath() { 123 return this.path; 124 } 125 126 /** 127 * Return the ClassLoader that this resource will be obtained from. 128 */ 129 @Nullable 130 public final ClassLoader getClassLoader() { 131 return (this.clazz != null ? this.clazz.getClassLoader() : this.classLoader); 132 } 133 134 135 /** 136 * This implementation checks for the resolution of a resource URL. 137 * @see java.lang.ClassLoader#getResource(String) 138 * @see java.lang.Class#getResource(String) 139 */ 140 @Override 141 public boolean exists() { 142 return (resolveURL() != null); 143 } 144 145 /** 146 * Resolves a URL for the underlying class path resource. 147 * @return the resolved URL, or {@code null} if not resolvable 148 */ 149 @Nullable 150 protected URL resolveURL() { 151 if (this.clazz != null) { 152 return this.clazz.getResource(this.path); 153 } 154 else if (this.classLoader != null) { 155 return this.classLoader.getResource(this.path); 156 } 157 else { 158 return ClassLoader.getSystemResource(this.path); 159 } 160 } 161 162 /** 163 * This implementation opens an InputStream for the given class path resource. 164 * @see java.lang.ClassLoader#getResourceAsStream(String) 165 * @see java.lang.Class#getResourceAsStream(String) 166 */ 167 @Override 168 public InputStream getInputStream() throws IOException { 169 InputStream is; 170 if (this.clazz != null) { 171 is = this.clazz.getResourceAsStream(this.path); 172 } 173 else if (this.classLoader != null) { 174 is = this.classLoader.getResourceAsStream(this.path); 175 } 176 else { 177 is = ClassLoader.getSystemResourceAsStream(this.path); 178 } 179 if (is == null) { 180 throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist"); 181 } 182 return is; 183 } 184 185 /** 186 * This implementation returns a URL for the underlying class path resource, 187 * if available. 188 * @see java.lang.ClassLoader#getResource(String) 189 * @see java.lang.Class#getResource(String) 190 */ 191 @Override 192 public URL getURL() throws IOException { 193 URL url = resolveURL(); 194 if (url == null) { 195 throw new FileNotFoundException(getDescription() + " cannot be resolved to URL because it does not exist"); 196 } 197 return url; 198 } 199 200 /** 201 * This implementation creates a ClassPathResource, applying the given path 202 * relative to the path of the underlying resource of this descriptor. 203 * @see org.springframework.util.StringUtils#applyRelativePath(String, String) 204 */ 205 @Override 206 public Resource createRelative(String relativePath) { 207 String pathToUse = StringUtils.applyRelativePath(this.path, relativePath); 208 return (this.clazz != null ? new ClassPathResource(pathToUse, this.clazz) : 209 new ClassPathResource(pathToUse, this.classLoader)); 210 } 211 212 /** 213 * This implementation returns the name of the file that this class path 214 * resource refers to. 215 * @see org.springframework.util.StringUtils#getFilename(String) 216 */ 217 @Override 218 @Nullable 219 public String getFilename() { 220 return StringUtils.getFilename(this.path); 221 } 222 223 /** 224 * This implementation returns a description that includes the class path location. 225 */ 226 @Override 227 public String getDescription() { 228 StringBuilder builder = new StringBuilder("class path resource ["); 229 String pathToUse = this.path; 230 if (this.clazz != null && !pathToUse.startsWith("/")) { 231 builder.append(ClassUtils.classPackageAsResourcePath(this.clazz)); 232 builder.append('/'); 233 } 234 if (pathToUse.startsWith("/")) { 235 pathToUse = pathToUse.substring(1); 236 } 237 builder.append(pathToUse); 238 builder.append(']'); 239 return builder.toString(); 240 } 241 242 243 /** 244 * This implementation compares the underlying class path locations. 245 */ 246 @Override 247 public boolean equals(@Nullable Object other) { 248 if (this == other) { 249 return true; 250 } 251 if (!(other instanceof ClassPathResource)) { 252 return false; 253 } 254 ClassPathResource otherRes = (ClassPathResource) other; 255 return (this.path.equals(otherRes.path) && 256 ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) && 257 ObjectUtils.nullSafeEquals(this.clazz, otherRes.clazz)); 258 } 259 260 /** 261 * This implementation returns the hash code of the underlying 262 * class path location. 263 */ 264 @Override 265 public int hashCode() { 266 return this.path.hashCode(); 267 } 268 269}