1
17 package org.apache.catalina.webresources;
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.net.URL;
23 import java.util.ArrayList;
24 import java.util.Map;
25 import java.util.Set;
26 import java.util.jar.JarEntry;
27 import java.util.jar.JarFile;
28 import java.util.jar.Manifest;
29
30 import org.apache.catalina.WebResource;
31 import org.apache.catalina.WebResourceRoot;
32 import org.apache.catalina.util.ResourceSet;
33 import org.apache.tomcat.util.compat.JreCompat;
34
35 public abstract class AbstractArchiveResourceSet extends AbstractResourceSet {
36
37 private URL baseUrl;
38 private String baseUrlString;
39
40 private JarFile archive = null;
41 protected Map<String,JarEntry> archiveEntries = null;
42 protected final Object archiveLock = new Object();
43 private long archiveUseCount = 0;
44
45
46 protected final void setBaseUrl(URL baseUrl) {
47 this.baseUrl = baseUrl;
48 if (baseUrl == null) {
49 this.baseUrlString = null;
50 } else {
51 this.baseUrlString = baseUrl.toString();
52 }
53 }
54
55 @Override
56 public final URL getBaseUrl() {
57 return baseUrl;
58 }
59
60 protected final String getBaseUrlString() {
61 return baseUrlString;
62 }
63
64
65 @Override
66 public final String[] list(String path) {
67 checkPath(path);
68 String webAppMount = getWebAppMount();
69
70 ArrayList<String> result = new ArrayList<>();
71 if (path.startsWith(webAppMount)) {
72 String pathInJar =
73 getInternalPath() + path.substring(webAppMount.length());
74
75 if (pathInJar.length() > 0 && pathInJar.charAt(0) == '/') {
76 pathInJar = pathInJar.substring(1);
77 }
78 for (String name : getArchiveEntries(false).keySet()) {
79 if (name.length() > pathInJar.length() &&
80 name.startsWith(pathInJar)) {
81 if (name.charAt(name.length() - 1) == '/') {
82 name = name.substring(
83 pathInJar.length(), name.length() - 1);
84 } else {
85 name = name.substring(pathInJar.length());
86 }
87 if (name.length() == 0) {
88 continue;
89 }
90 if (name.charAt(0) == '/') {
91 name = name.substring(1);
92 }
93 if (name.length() > 0 && name.lastIndexOf('/') == -1) {
94 result.add(name);
95 }
96 }
97 }
98 } else {
99 if (!path.endsWith("/")) {
100 path = path + "/";
101 }
102 if (webAppMount.startsWith(path)) {
103 int i = webAppMount.indexOf('/', path.length());
104 if (i == -1) {
105 return new String[] {webAppMount.substring(path.length())};
106 } else {
107 return new String[] {
108 webAppMount.substring(path.length(), i)};
109 }
110 }
111 }
112 return result.toArray(new String[result.size()]);
113 }
114
115 @Override
116 public final Set<String> listWebAppPaths(String path) {
117 checkPath(path);
118 String webAppMount = getWebAppMount();
119
120 ResourceSet<String> result = new ResourceSet<>();
121 if (path.startsWith(webAppMount)) {
122 String pathInJar =
123 getInternalPath() + path.substring(webAppMount.length());
124
125
126 if (pathInJar.length() > 0) {
127 if (pathInJar.charAt(pathInJar.length() - 1) != '/') {
128 pathInJar = pathInJar.substring(1) + '/';
129 }
130 if (pathInJar.charAt(0) == '/') {
131 pathInJar = pathInJar.substring(1);
132 }
133 }
134
135 for (String name : getArchiveEntries(false).keySet()) {
136 if (name.length() > pathInJar.length() && name.startsWith(pathInJar)) {
137 int nextSlash = name.indexOf('/', pathInJar.length());
138 if (nextSlash != -1 && nextSlash != name.length() - 1) {
139 name = name.substring(0, nextSlash + 1);
140 }
141 result.add(webAppMount + '/' + name.substring(getInternalPath().length()));
142 }
143 }
144 } else {
145 if (!path.endsWith("/")) {
146 path = path + "/";
147 }
148 if (webAppMount.startsWith(path)) {
149 int i = webAppMount.indexOf('/', path.length());
150 if (i == -1) {
151 result.add(webAppMount + "/");
152 } else {
153 result.add(webAppMount.substring(0, i + 1));
154 }
155 }
156 }
157 result.setLocked(true);
158 return result;
159 }
160
161
162
174 protected abstract Map<String,JarEntry> getArchiveEntries(boolean single);
175
176
177
187 protected abstract JarEntry getArchiveEntry(String pathInArchive);
188
189 @Override
190 public final boolean mkdir(String path) {
191 checkPath(path);
192
193 return false;
194 }
195
196 @Override
197 public final boolean write(String path, InputStream is, boolean overwrite) {
198 checkPath(path);
199
200 if (is == null) {
201 throw new NullPointerException(
202 sm.getString("dirResourceSet.writeNpe"));
203 }
204
205 return false;
206 }
207
208 @Override
209 public final WebResource getResource(String path) {
210 checkPath(path);
211 String webAppMount = getWebAppMount();
212 WebResourceRoot root = getRoot();
213
214
228
229
230
231
232 if (path.startsWith(webAppMount)) {
233 String pathInJar = getInternalPath() + path.substring(
234 webAppMount.length(), path.length());
235
236 if (pathInJar.length() > 0 && pathInJar.charAt(0) == '/') {
237 pathInJar = pathInJar.substring(1);
238 }
239 if (pathInJar.equals("")) {
240
241
242 if (!path.endsWith("/")) {
243 path = path + "/";
244 }
245 return new JarResourceRoot(root, new File(getBase()),
246 baseUrlString, path);
247 } else {
248 JarEntry jarEntry = null;
249 if (isMultiRelease()) {
250
251 jarEntry = getArchiveEntry(pathInJar);
252 } else {
253 Map<String,JarEntry> jarEntries = getArchiveEntries(true);
254 if (!(pathInJar.charAt(pathInJar.length() - 1) == '/')) {
255 if (jarEntries == null) {
256 jarEntry = getArchiveEntry(pathInJar + '/');
257 } else {
258 jarEntry = jarEntries.get(pathInJar + '/');
259 }
260 if (jarEntry != null) {
261 path = path + '/';
262 }
263 }
264 if (jarEntry == null) {
265 if (jarEntries == null) {
266 jarEntry = getArchiveEntry(pathInJar);
267 } else {
268 jarEntry = jarEntries.get(pathInJar);
269 }
270 }
271 }
272 if (jarEntry == null) {
273 return new EmptyResource(root, path);
274 } else {
275 return createArchiveResource(jarEntry, path, getManifest());
276 }
277 }
278 } else {
279 return new EmptyResource(root, path);
280 }
281 }
282
283 protected abstract boolean isMultiRelease();
284
285 protected abstract WebResource createArchiveResource(JarEntry jarEntry,
286 String webAppPath, Manifest manifest);
287
288 @Override
289 public final boolean isReadOnly() {
290 return true;
291 }
292
293 @Override
294 public void setReadOnly(boolean readOnly) {
295 if (readOnly) {
296
297 return;
298 }
299
300 throw new IllegalArgumentException(
301 sm.getString("abstractArchiveResourceSet.setReadOnlyFalse"));
302 }
303
304 protected JarFile openJarFile() throws IOException {
305 synchronized (archiveLock) {
306 if (archive == null) {
307 archive = JreCompat.getInstance().jarFileNewInstance(getBase());
308 }
309 archiveUseCount++;
310 return archive;
311 }
312 }
313
314 protected void closeJarFile() {
315 synchronized (archiveLock) {
316 archiveUseCount--;
317 }
318 }
319
320 @Override
321 public void gc() {
322 synchronized (archiveLock) {
323 if (archive != null && archiveUseCount == 0) {
324 try {
325 archive.close();
326 } catch (IOException e) {
327
328 }
329 archive = null;
330 archiveEntries = null;
331 }
332 }
333 }
334 }
335