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