1
17 package org.apache.catalina.webresources;
18
19 import java.io.ByteArrayInputStream;
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.net.MalformedURLException;
26 import java.net.URL;
27 import java.nio.charset.StandardCharsets;
28 import java.nio.file.Files;
29 import java.nio.file.attribute.BasicFileAttributes;
30 import java.security.cert.Certificate;
31 import java.util.jar.Manifest;
32
33 import org.apache.catalina.WebResourceRoot;
34 import org.apache.juli.logging.Log;
35 import org.apache.juli.logging.LogFactory;
36
37
41 public class FileResource extends AbstractResource {
42
43 private static final Log log = LogFactory.getLog(FileResource.class);
44
45 private static final boolean PROPERTIES_NEED_CONVERT;
46 static {
47 boolean isEBCDIC = false;
48 try {
49 String encoding = System.getProperty("file.encoding");
50 if (encoding.contains("EBCDIC")) {
51 isEBCDIC = true;
52 }
53 } catch (SecurityException e) {
54
55 }
56 PROPERTIES_NEED_CONVERT = isEBCDIC;
57 }
58
59
60 private final File resource;
61 private final String name;
62 private final boolean readOnly;
63 private final Manifest manifest;
64 private final boolean needConvert;
65
66 public FileResource(WebResourceRoot root, String webAppPath,
67 File resource, boolean readOnly, Manifest manifest) {
68 super(root,webAppPath);
69 this.resource = resource;
70
71 if (webAppPath.charAt(webAppPath.length() - 1) == '/') {
72 String realName = resource.getName() + '/';
73 if (webAppPath.endsWith(realName)) {
74 name = resource.getName();
75 } else {
76
77
78 int endOfName = webAppPath.length() - 1;
79 name = webAppPath.substring(
80 webAppPath.lastIndexOf('/', endOfName - 1) + 1,
81 endOfName);
82 }
83 } else {
84
85 name = resource.getName();
86 }
87
88 this.readOnly = readOnly;
89 this.manifest = manifest;
90 this.needConvert = PROPERTIES_NEED_CONVERT && name.endsWith(".properties");
91 }
92
93 @Override
94 public long getLastModified() {
95 return resource.lastModified();
96 }
97
98 @Override
99 public boolean exists() {
100 return resource.exists();
101 }
102
103 @Override
104 public boolean isVirtual() {
105 return false;
106 }
107
108 @Override
109 public boolean isDirectory() {
110 return resource.isDirectory();
111 }
112
113 @Override
114 public boolean isFile() {
115 return resource.isFile();
116 }
117
118 @Override
119 public boolean delete() {
120 if (readOnly) {
121 return false;
122 }
123 return resource.delete();
124 }
125
126 @Override
127 public String getName() {
128 return name;
129 }
130
131 @Override
132 public long getContentLength() {
133 return getContentLengthInternal(needConvert);
134 }
135
136 private long getContentLengthInternal(boolean convert) {
137 if (convert) {
138 byte[] content = getContent();
139 if (content == null) {
140 return -1;
141 } else {
142 return content.length;
143 }
144 }
145
146 if (isDirectory()) {
147 return -1;
148 }
149
150 return resource.length();
151 }
152
153 @Override
154 public String getCanonicalPath() {
155 try {
156 return resource.getCanonicalPath();
157 } catch (IOException ioe) {
158 if (log.isDebugEnabled()) {
159 log.debug(sm.getString("fileResource.getCanonicalPathFail",
160 resource.getPath()), ioe);
161 }
162 return null;
163 }
164 }
165
166 @Override
167 public boolean canRead() {
168 return resource.canRead();
169 }
170
171 @Override
172 protected InputStream doGetInputStream() {
173 if (needConvert) {
174 byte[] content = getContent();
175 if (content == null) {
176 return null;
177 } else {
178 return new ByteArrayInputStream(content);
179 }
180 }
181 try {
182 return new FileInputStream(resource);
183 } catch (FileNotFoundException fnfe) {
184
185 return null;
186 }
187 }
188
189 @Override
190 public final byte[] getContent() {
191
192 long len = getContentLengthInternal(false);
193
194 if (len > Integer.MAX_VALUE) {
195
196 throw new ArrayIndexOutOfBoundsException(sm.getString(
197 "abstractResource.getContentTooLarge", getWebappPath(),
198 Long.valueOf(len)));
199 }
200
201 if (len < 0) {
202
203 return null;
204 }
205
206 int size = (int) len;
207 byte[] result = new byte[size];
208
209 int pos = 0;
210 try (InputStream is = new FileInputStream(resource)) {
211 while (pos < size) {
212 int n = is.read(result, pos, size - pos);
213 if (n < 0) {
214 break;
215 }
216 pos += n;
217 }
218 } catch (IOException ioe) {
219 if (getLog().isDebugEnabled()) {
220 getLog().debug(sm.getString("abstractResource.getContentFail",
221 getWebappPath()), ioe);
222 }
223 return null;
224 }
225
226 if (needConvert) {
227
228
229
230
231 String str = new String(result);
232 try {
233 result = str.getBytes(StandardCharsets.UTF_8);
234 } catch (Exception e) {
235 result = null;
236 }
237 }
238 return result;
239 }
240
241
242 @Override
243 public long getCreation() {
244 try {
245 BasicFileAttributes attrs = Files.readAttributes(resource.toPath(),
246 BasicFileAttributes.class);
247 return attrs.creationTime().toMillis();
248 } catch (IOException e) {
249 if (log.isDebugEnabled()) {
250 log.debug(sm.getString("fileResource.getCreationFail",
251 resource.getPath()), e);
252 }
253 return 0;
254 }
255 }
256
257 @Override
258 public URL getURL() {
259 if (resource.exists()) {
260 try {
261 return resource.toURI().toURL();
262 } catch (MalformedURLException e) {
263 if (log.isDebugEnabled()) {
264 log.debug(sm.getString("fileResource.getUrlFail",
265 resource.getPath()), e);
266 }
267 return null;
268 }
269 } else {
270 return null;
271 }
272 }
273
274 @Override
275 public URL getCodeBase() {
276 if (getWebappPath().startsWith("/WEB-INF/classes/") && name.endsWith(".class")) {
277 return getWebResourceRoot().getResource("/WEB-INF/classes/").getURL();
278 } else {
279 return getURL();
280 }
281 }
282
283 @Override
284 public Certificate[] getCertificates() {
285 return null;
286 }
287
288 @Override
289 public Manifest getManifest() {
290 return manifest;
291 }
292
293 @Override
294 protected Log getLog() {
295 return log;
296 }
297 }
298