1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.springframework.extensions.webscripts;
20
21 import java.io.File;
22 import java.io.FileNotFoundException;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.InputStreamReader;
27 import java.io.OutputStream;
28 import java.io.PrintWriter;
29 import java.io.Reader;
30 import java.io.UnsupportedEncodingException;
31 import java.util.ArrayList;
32 import java.util.List;
33
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36 import org.springframework.beans.BeansException;
37 import org.springframework.context.ApplicationContext;
38 import org.springframework.context.ApplicationContextAware;
39 import org.springframework.core.io.FileSystemResource;
40 import org.springframework.core.io.Resource;
41 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
42 import org.springframework.core.io.support.ResourcePatternResolver;
43 import org.springframework.extensions.surf.exception.WebScriptsPlatformException;
44
45 import freemarker.cache.ClassTemplateLoader;
46 import freemarker.cache.TemplateLoader;
47
48
49
50
51
52
53
54 public class ClassPathStore extends AbstractStore implements ApplicationContextAware, Store
55 {
56
57 private static final Log logger = LogFactory.getLog(ClassPathStore.class);
58
59 ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
60 protected boolean mustExist = false;
61 protected String classPath;
62 protected Resource storeResource;
63 protected String storeResourcePath;
64 protected int storeResourcePathLength;
65 protected File storeDir;
66
67
68
69
70
71
72
73
74
75
76 public void setMustExist(boolean mustExist)
77 {
78 this.mustExist = mustExist;
79 }
80
81
82
83
84
85
86 public void setClassPath(String classPath)
87 {
88 String cleanClassPath = (classPath.endsWith("/")) ? classPath.substring(0, classPath.length() -1) : classPath;
89 this.classPath = cleanClassPath;
90 }
91
92
93
94
95
96 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
97 {
98 this.resolver = applicationContext;
99 }
100
101
102
103
104 public void init()
105 {
106 try
107 {
108
109
110
111 Resource rootResource = null;
112 Resource[] resources = resolver.getResources("classpath*:" + classPath + "*");
113 for (Resource resource : resources)
114 {
115 String externalForm = resource.getURL().toExternalForm();
116 if (externalForm.endsWith(classPath) || externalForm.endsWith(classPath + "/"))
117 {
118
119
120 String directoryPath = resource.getFile().getAbsolutePath();
121 if (resource.getFile().isDirectory() && !directoryPath.endsWith("/"))
122 {
123 directoryPath += "/";
124 }
125 rootResource = new FileSystemResource(directoryPath);
126
127 break;
128 }
129 }
130
131 if (rootResource != null && rootResource.exists())
132 {
133 storeResource = rootResource;
134 storeResourcePath = storeResource.getURL().toExternalForm();
135 String cleanStoreResourcePath = (storeResourcePath.endsWith("/")) ? storeResourcePath.substring(0, storeResourcePath.length() -1) : storeResourcePath;
136 storeResourcePathLength = cleanStoreResourcePath.length();
137 if (logger.isTraceEnabled())
138 logger.trace("Provided classpath: " + classPath + " , storeRootPath: " + storeResourcePath + ", storeRootPathLength: " + storeResourcePathLength);
139
140 try
141 {
142
143 storeDir = resources[0].getFile();
144 }
145 catch(FileNotFoundException e)
146 {
147
148 }
149 }
150 else if (mustExist)
151 {
152 throw new WebScriptException("Web Script Store classpath:" + classPath + " must exist; it was not found");
153 }
154 }
155 catch(IOException e)
156 {
157 throw new WebScriptException("Failed to initialise Web Script Store classpath: " + classPath, e);
158 }
159 }
160
161
162
163
164 public boolean exists()
165 {
166 return (storeResource != null);
167 }
168
169
170
171
172 public String getBasePath()
173 {
174 return "classpath:" + classPath;
175 }
176
177
178
179
180
181 public boolean isSecure()
182 {
183 return true;
184 }
185
186
187
188
189 public String[] getAllDocumentPaths()
190 {
191 String[] paths;
192
193 try
194 {
195 List<String> documentPaths = getPaths("classpath*:" + classPath + "/**/*");
196 paths = documentPaths.toArray(new String[documentPaths.size()]);
197 }
198 catch (IOException e)
199 {
200
201 paths = new String[0];
202 }
203
204 return paths;
205 }
206
207
208
209
210 public String[] getDocumentPaths(String path, boolean includeSubPaths, String documentPattern)
211 throws IOException
212 {
213 if ((path == null) || (path.length() == 0))
214 {
215 path = "/";
216 }
217
218 if (! path.startsWith("/"))
219 {
220 path = "/" + path;
221 }
222
223 if (! path.endsWith("/"))
224 {
225 path = path + "/";
226 }
227
228 if ((documentPattern == null) || (documentPattern.length() == 0))
229 {
230 documentPattern = "*";
231 }
232
233 final StringBuilder pattern = new StringBuilder(128);
234 pattern.append("classpath*:").append(classPath)
235 .append(path)
236 .append((includeSubPaths ? "**/" : ""))
237 .append(documentPattern);
238
239 List<String> documentPaths = getPaths(pattern.toString());
240 return documentPaths.toArray(new String[documentPaths.size()]);
241 }
242
243
244
245
246 public String[] getDescriptionDocumentPaths() throws IOException
247 {
248 return getDocumentPaths("/", true, "*.desc.xml");
249 }
250
251
252
253
254 public String[] getScriptDocumentPaths(WebScript script) throws IOException
255 {
256 String scriptPaths = script.getDescription().getId() + ".*";
257 return getDocumentPaths("/", false, scriptPaths);
258 }
259
260
261
262
263 private List<String> getPaths(String pattern)
264 throws IOException
265 {
266 Resource[] resources = resolver.getResources(pattern);
267 List<String> documentPaths = new ArrayList<String>(resources.length);
268 for (Resource resource : resources)
269 {
270
271 try
272 {
273 if (resource.getFile().isFile())
274 {
275 if (resource.getURL().toExternalForm().startsWith(storeResourcePath))
276 {
277 String resourcePath = resource.getURL().toExternalForm();
278 String documentPath = resourcePath.substring(storeResourcePathLength +1);
279 documentPath = documentPath.replace('\\', '/');
280 if (logger.isTraceEnabled())
281 logger.trace("Item resource path: " + resourcePath + " , item path: " + documentPath);
282 documentPaths.add(documentPath);
283 }
284 }
285 }
286 catch (IOException ioe)
287 {
288
289 }
290 }
291 return documentPaths;
292 }
293
294
295
296
297 public long lastModified(String documentPath)
298 throws IOException
299 {
300 Resource document = createRelative(storeResource, documentPath);
301 return document.getURL().openConnection().getLastModified();
302 }
303
304
305
306
307 public boolean hasDocument(String documentPath)
308 {
309 boolean exists = false;
310 try
311 {
312 Resource document = createRelative(storeResource, documentPath);
313 exists = document.exists();
314 }
315 catch(IOException e)
316 {
317 }
318 return exists;
319 }
320
321
322
323
324 public InputStream getDocument(String documentPath)
325 throws IOException
326 {
327 Resource document = createRelative(storeResource, documentPath);
328 if (logger.isTraceEnabled())
329 logger.trace("getDocument: documentPath: " + documentPath + " , storePath: " + document.getURL().toExternalForm());
330
331 if (!document.exists())
332 {
333 throw new IOException("Document " + documentPath + " does not exist within store " + getBasePath());
334 }
335 return document.getInputStream();
336 }
337
338
339
340
341 public void createDocument(String documentPath, String content) throws IOException
342 {
343 File document = new File(storeDir, documentPath);
344
345
346 File path = document.getParentFile();
347 path.mkdirs();
348
349
350 if (!document.createNewFile())
351 {
352 throw new IOException("Document " + documentPath + " already exists");
353 }
354 OutputStream output = new FileOutputStream(document);
355 try
356 {
357 PrintWriter writer = new PrintWriter(output);
358 writer.write(content);
359 writer.flush();
360 }
361 finally
362 {
363 output.flush();
364 output.close();
365 }
366 }
367
368
369
370
371 public void updateDocument(String documentPath, String content) throws IOException
372 {
373 File document = new File(storeDir, documentPath);
374
375
376 if (!document.canWrite())
377 {
378 throw new IOException("Document " + documentPath + " is not writable");
379 }
380 OutputStream output = new FileOutputStream(document);
381 try
382 {
383 PrintWriter writer = new PrintWriter(output);
384 writer.write(content);
385 writer.flush();
386 }
387 finally
388 {
389 output.flush();
390 output.close();
391 }
392 }
393
394
395
396
397 public boolean removeDocument(String documentPath)
398 throws IOException
399 {
400
401
402 return false;
403 }
404
405
406
407
408 public TemplateLoader getTemplateLoader()
409 {
410
411 String templateClassPath = (classPath.charAt(0) == '/') ? classPath : "/" + classPath;
412 return new ClassTemplateLoader(ClassPathStore.class, templateClassPath);
413 }
414
415
416
417
418 public ScriptLoader getScriptLoader()
419 {
420 return new ClassPathScriptLoader();
421 }
422
423
424
425
426
427
428
429
430
431 private Resource createRelative(Resource resource, String path)
432 throws IOException
433 {
434 if (storeResourcePath.endsWith("/"))
435 {
436 return resource.createRelative(path);
437 }
438
439 int prefixIdx = storeResourcePath.lastIndexOf("/");
440 String prefix = (prefixIdx != -1) ? storeResourcePath.substring(prefixIdx) : "";
441 return resource.createRelative(prefix + "/" + path);
442 }
443
444
445
446
447
448
449
450
451 public static String createPath(String path, String relativePath)
452 {
453 if (path.endsWith("/"))
454 {
455 path = path.substring(0, path.length() - 1);
456 }
457
458 if (relativePath.startsWith("/"))
459 {
460 relativePath = relativePath.substring(1);
461 }
462
463 return path + "/" + relativePath;
464 }
465
466
467
468
469
470 @Override
471 public String toString()
472 {
473 return this.storeResourcePath;
474 }
475
476
477
478
479
480
481
482 private class ClassPathScriptLoader implements ScriptLoader
483 {
484
485
486
487
488 public ScriptContent getScript(String path)
489 {
490 ScriptContent location = null;
491 try
492 {
493 Resource script = createRelative(storeResource, path);
494 if (script.exists())
495 {
496 location = new ClassPathScriptLocation(storeResource, path, script);
497 }
498 }
499 catch(IOException e)
500 {
501 }
502 return location;
503 }
504 }
505
506
507
508
509
510
511 private static class ClassPathScriptLocation implements ScriptContent
512 {
513 private Resource store;
514 private String path;
515 private Resource location;
516
517
518
519
520
521
522
523
524 public ClassPathScriptLocation(Resource store, String path, Resource location)
525 {
526 this.store = store;
527 this.path = path;
528 this.location = location;
529 }
530
531
532
533
534 public InputStream getInputStream()
535 {
536 try
537 {
538 return location.getInputStream();
539 }
540 catch (IOException e)
541 {
542 throw new WebScriptException("Unable to retrieve input stream for script " + getPathDescription());
543 }
544 }
545
546
547
548
549 public Reader getReader()
550 {
551 try
552 {
553 return new InputStreamReader(getInputStream(), "UTF-8");
554 }
555 catch (UnsupportedEncodingException e)
556 {
557 throw new WebScriptsPlatformException("Unsupported Encoding", e);
558 }
559 }
560
561
562
563
564 public String getPath()
565 {
566 String path = "<unknown path>";
567 try
568 {
569 path = location.getURL().toExternalForm();
570 }
571 catch(IOException ioe)
572 {
573 };
574 return path;
575 }
576
577
578
579
580 public String getPathDescription()
581 {
582 String desc = "<unknown path>";
583 try
584 {
585 desc = "/" + path + " (in classpath store " + store.getURL().toExternalForm() + ")";
586 }
587 catch(IOException ioe)
588 {
589 };
590 return desc;
591 }
592
593
594
595
596 public boolean isCachable()
597 {
598 return true;
599 }
600
601
602
603
604 public boolean isSecure()
605 {
606 return true;
607 }
608
609 @Override
610 public String toString()
611 {
612 return getPathDescription();
613 }
614 }
615 }