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