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.web.context.support; 018 019import java.io.File; 020import java.io.FileNotFoundException; 021import java.io.IOException; 022import java.io.InputStream; 023import java.net.MalformedURLException; 024import java.net.URL; 025 026import javax.servlet.ServletContext; 027 028import org.springframework.core.io.AbstractFileResolvingResource; 029import org.springframework.core.io.ContextResource; 030import org.springframework.core.io.Resource; 031import org.springframework.lang.Nullable; 032import org.springframework.util.Assert; 033import org.springframework.util.ResourceUtils; 034import org.springframework.util.StringUtils; 035import org.springframework.web.util.WebUtils; 036 037/** 038 * {@link org.springframework.core.io.Resource} implementation for 039 * {@link javax.servlet.ServletContext} resources, interpreting 040 * relative paths within the web application root directory. 041 * 042 * <p>Always supports stream access and URL access, but only allows 043 * {@code java.io.File} access when the web application archive 044 * is expanded. 045 * 046 * @author Juergen Hoeller 047 * @since 28.12.2003 048 * @see javax.servlet.ServletContext#getResourceAsStream 049 * @see javax.servlet.ServletContext#getResource 050 * @see javax.servlet.ServletContext#getRealPath 051 */ 052public class ServletContextResource extends AbstractFileResolvingResource implements ContextResource { 053 054 private final ServletContext servletContext; 055 056 private final String path; 057 058 059 /** 060 * Create a new ServletContextResource. 061 * <p>The Servlet spec requires that resource paths start with a slash, 062 * even if many containers accept paths without leading slash too. 063 * Consequently, the given path will be prepended with a slash if it 064 * doesn't already start with one. 065 * @param servletContext the ServletContext to load from 066 * @param path the path of the resource 067 */ 068 public ServletContextResource(ServletContext servletContext, String path) { 069 // check ServletContext 070 Assert.notNull(servletContext, "Cannot resolve ServletContextResource without ServletContext"); 071 this.servletContext = servletContext; 072 073 // check path 074 Assert.notNull(path, "Path is required"); 075 String pathToUse = StringUtils.cleanPath(path); 076 if (!pathToUse.startsWith("/")) { 077 pathToUse = "/" + pathToUse; 078 } 079 this.path = pathToUse; 080 } 081 082 083 /** 084 * Return the ServletContext for this resource. 085 */ 086 public final ServletContext getServletContext() { 087 return this.servletContext; 088 } 089 090 /** 091 * Return the path for this resource. 092 */ 093 public final String getPath() { 094 return this.path; 095 } 096 097 /** 098 * This implementation checks {@code ServletContext.getResource}. 099 * @see javax.servlet.ServletContext#getResource(String) 100 */ 101 @Override 102 public boolean exists() { 103 try { 104 URL url = this.servletContext.getResource(this.path); 105 return (url != null); 106 } 107 catch (MalformedURLException ex) { 108 return false; 109 } 110 } 111 112 /** 113 * This implementation delegates to {@code ServletContext.getResourceAsStream}, 114 * which returns {@code null} in case of a non-readable resource (e.g. a directory). 115 * @see javax.servlet.ServletContext#getResourceAsStream(String) 116 */ 117 @Override 118 public boolean isReadable() { 119 InputStream is = this.servletContext.getResourceAsStream(this.path); 120 if (is != null) { 121 try { 122 is.close(); 123 } 124 catch (IOException ex) { 125 // ignore 126 } 127 return true; 128 } 129 else { 130 return false; 131 } 132 } 133 134 @Override 135 public boolean isFile() { 136 try { 137 URL url = this.servletContext.getResource(this.path); 138 if (url != null && ResourceUtils.isFileURL(url)) { 139 return true; 140 } 141 else { 142 return (this.servletContext.getRealPath(this.path) != null); 143 } 144 } 145 catch (MalformedURLException ex) { 146 return false; 147 } 148 } 149 150 /** 151 * This implementation delegates to {@code ServletContext.getResourceAsStream}, 152 * but throws a FileNotFoundException if no resource found. 153 * @see javax.servlet.ServletContext#getResourceAsStream(String) 154 */ 155 @Override 156 public InputStream getInputStream() throws IOException { 157 InputStream is = this.servletContext.getResourceAsStream(this.path); 158 if (is == null) { 159 throw new FileNotFoundException("Could not open " + getDescription()); 160 } 161 return is; 162 } 163 164 /** 165 * This implementation delegates to {@code ServletContext.getResource}, 166 * but throws a FileNotFoundException if no resource found. 167 * @see javax.servlet.ServletContext#getResource(String) 168 */ 169 @Override 170 public URL getURL() throws IOException { 171 URL url = this.servletContext.getResource(this.path); 172 if (url == null) { 173 throw new FileNotFoundException( 174 getDescription() + " cannot be resolved to URL because it does not exist"); 175 } 176 return url; 177 } 178 179 /** 180 * This implementation resolves "file:" URLs or alternatively delegates to 181 * {@code ServletContext.getRealPath}, throwing a FileNotFoundException 182 * if not found or not resolvable. 183 * @see javax.servlet.ServletContext#getResource(String) 184 * @see javax.servlet.ServletContext#getRealPath(String) 185 */ 186 @Override 187 public File getFile() throws IOException { 188 URL url = this.servletContext.getResource(this.path); 189 if (url != null && ResourceUtils.isFileURL(url)) { 190 // Proceed with file system resolution... 191 return super.getFile(); 192 } 193 else { 194 String realPath = WebUtils.getRealPath(this.servletContext, this.path); 195 return new File(realPath); 196 } 197 } 198 199 /** 200 * This implementation creates a ServletContextResource, applying the given path 201 * relative to the path of the underlying file of this resource descriptor. 202 * @see org.springframework.util.StringUtils#applyRelativePath(String, String) 203 */ 204 @Override 205 public Resource createRelative(String relativePath) { 206 String pathToUse = StringUtils.applyRelativePath(this.path, relativePath); 207 return new ServletContextResource(this.servletContext, pathToUse); 208 } 209 210 /** 211 * This implementation returns the name of the file that this ServletContext 212 * resource refers to. 213 * @see org.springframework.util.StringUtils#getFilename(String) 214 */ 215 @Override 216 @Nullable 217 public String getFilename() { 218 return StringUtils.getFilename(this.path); 219 } 220 221 /** 222 * This implementation returns a description that includes the ServletContext 223 * resource location. 224 */ 225 @Override 226 public String getDescription() { 227 return "ServletContext resource [" + this.path + "]"; 228 } 229 230 @Override 231 public String getPathWithinContext() { 232 return this.path; 233 } 234 235 236 /** 237 * This implementation compares the underlying ServletContext resource locations. 238 */ 239 @Override 240 public boolean equals(@Nullable Object other) { 241 if (this == other) { 242 return true; 243 } 244 if (!(other instanceof ServletContextResource)) { 245 return false; 246 } 247 ServletContextResource otherRes = (ServletContextResource) other; 248 return (this.servletContext.equals(otherRes.servletContext) && this.path.equals(otherRes.path)); 249 } 250 251 /** 252 * This implementation returns the hash code of the underlying 253 * ServletContext resource location. 254 */ 255 @Override 256 public int hashCode() { 257 return this.path.hashCode(); 258 } 259 260}