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}