1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.jetspeed.page.document.psml;
18
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.FileNotFoundException;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
26 import java.io.OutputStreamWriter;
27 import java.io.Writer;
28
29 import javax.xml.parsers.ParserConfigurationException;
30 import javax.xml.parsers.SAXParser;
31 import javax.xml.parsers.SAXParserFactory;
32
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.apache.jetspeed.cache.file.FileCache;
36 import org.apache.jetspeed.cache.file.FileCacheEntry;
37 import org.apache.jetspeed.cache.file.FileCacheEventListener;
38 import org.apache.jetspeed.om.common.SecurityConstraints;
39 import org.apache.jetspeed.om.folder.psml.FolderImpl;
40 import org.apache.jetspeed.om.page.Document;
41 import org.apache.jetspeed.om.page.psml.AbstractBaseElement;
42 import org.apache.jetspeed.page.PageNotFoundException;
43 import org.apache.jetspeed.page.document.DocumentException;
44 import org.apache.jetspeed.page.document.DocumentHandlerFactory;
45 import org.apache.jetspeed.page.document.DocumentNotFoundException;
46 import org.apache.jetspeed.page.document.FailedToDeleteDocumentException;
47 import org.apache.jetspeed.page.document.FailedToUpdateDocumentException;
48 import org.apache.jetspeed.page.document.Node;
49 import org.apache.jetspeed.page.document.NodeException;
50 import org.apache.xml.serialize.OutputFormat;
51 import org.apache.xml.serialize.Serializer;
52 import org.apache.xml.serialize.XMLSerializer;
53 import org.castor.mapping.BindingType;
54 import org.castor.mapping.MappingUnmarshaller;
55 import org.exolab.castor.mapping.Mapping;
56 import org.exolab.castor.mapping.MappingException;
57 import org.exolab.castor.mapping.MappingLoader;
58 import org.exolab.castor.xml.ClassDescriptorResolver;
59 import org.exolab.castor.xml.ClassDescriptorResolverFactory;
60 import org.exolab.castor.xml.MarshalException;
61 import org.exolab.castor.xml.Marshaller;
62 import org.exolab.castor.xml.SAX2EventProducer;
63 import org.exolab.castor.xml.Unmarshaller;
64 import org.exolab.castor.xml.ValidationException;
65 import org.exolab.castor.xml.XMLClassDescriptorResolver;
66 import org.xml.sax.Attributes;
67 import org.xml.sax.ContentHandler;
68 import org.xml.sax.InputSource;
69 import org.xml.sax.Locator;
70 import org.xml.sax.SAXException;
71 import org.xml.sax.XMLReader;
72
73 /***
74 * <p>
75 * CastorFileSystemDocumentHandler
76 * </p>
77 * <p>
78 *
79 * </p>
80 *
81 * @author <a href="mailto:weaver@apache.org">Scott T. Weaver </a>
82 * @version $Id: CastorFileSystemDocumentHandler.java 551606 2007-06-28 16:07:53Z taylor $
83 *
84 */
85 public class CastorFileSystemDocumentHandler implements org.apache.jetspeed.page.document.DocumentHandler, FileCacheEventListener
86 {
87 private final static Log log = LogFactory.getLog(CastorFileSystemDocumentHandler.class);
88
89 private final static String PSML_DOCUMENT_ENCODING = "UTF-8";
90
91 protected String documentType;
92 protected Class expectedReturnType;
93 protected String documentRoot;
94 protected File documentRootDir;
95 protected FileCache fileCache;
96
97 private OutputFormat format;
98 private final XMLReader xmlReader;
99 private DocumentHandlerFactory handlerFactory;
100 private ClassDescriptorResolver classDescriptorResolver;
101
102 /***
103 *
104 * @param mappingFile
105 * Castor mapping file. THe mapping file must be in the class
106 * path
107 * @param documentType
108 * @param expectedReturnType
109 * @throws FileNotFoundException
110 */
111 public CastorFileSystemDocumentHandler( String mappingFile, String documentType, Class expectedReturnType,
112 String documentRoot, FileCache fileCache ) throws FileNotFoundException,SAXException,ParserConfigurationException, MappingException
113 {
114 super();
115 this.documentType = documentType;
116 this.expectedReturnType = expectedReturnType;
117 this.documentRoot = documentRoot;
118 this.documentRootDir = new File(documentRoot);
119 verifyPath(documentRootDir);
120 this.fileCache = fileCache;
121 this.fileCache.addListener(this);
122 this.format = new OutputFormat();
123 format.setIndenting(true);
124 format.setIndent(4);
125 format.setEncoding(PSML_DOCUMENT_ENCODING);
126
127 SAXParserFactory factory = SAXParserFactory.newInstance();
128 SAXParser parser = factory.newSAXParser();
129
130 xmlReader = parser.getXMLReader();
131 xmlReader.setFeature("http://xml.org/sax/features/namespaces", false);
132
133
134
135
136
137 createCastorClassDescriptorResolver(mappingFile);
138 }
139
140 public CastorFileSystemDocumentHandler( String mappingFile, String documentType, String expectedReturnType,
141 String documentRoot, FileCache fileCache ) throws FileNotFoundException, ClassNotFoundException,SAXException,ParserConfigurationException, MappingException
142 {
143 this(mappingFile, documentType, Class.forName(expectedReturnType), documentRoot, fileCache);
144 }
145
146 /***
147 * <p>
148 * getDocument
149 * </p>
150 *
151 * @see org.apache.jetspeed.page.document.DocumentHandler#getDocument(java.lang.String)
152 * @param name
153 * @return @throws
154 * DocumentNotFoundException
155 * @throws DocumentException,
156 * DocumentNotFoundException
157 */
158 public Document getDocument( String name ) throws NodeException, DocumentNotFoundException
159 {
160 return getDocument(name, true);
161 }
162
163 public void updateDocument( Document document ) throws FailedToUpdateDocumentException
164 {
165 updateDocument(document, false);
166 }
167
168 /***
169 * <p>
170 * updateDocument
171 * </p>
172 *
173 * @see org.apache.jetspeed.page.document.DocumentHandler#updateDocument(org.apache.jetspeed.om.page.Document)
174 * @param document
175 * @param systemUpdate
176 */
177 protected void updateDocument( Document document, boolean systemUpdate) throws FailedToUpdateDocumentException
178 {
179
180 if (document == null)
181 {
182 log.warn("Recieved null Document to update");
183 return;
184 }
185 String path = document.getPath();
186 if (path == null)
187 {
188 path = document.getId();
189 if (path == null)
190 {
191 log.warn("Recieved Document with null path/id to update");
192 return;
193 }
194 document.setPath(path);
195 }
196 AbstractBaseElement documentImpl = (AbstractBaseElement)document;
197 documentImpl.setHandlerFactory(handlerFactory);
198 if (systemUpdate){
199
200 documentImpl.setPermissionsEnabled(false);
201 documentImpl.setConstraintsEnabled(false);
202 } else {
203 documentImpl.setPermissionsEnabled(handlerFactory.getPermissionsEnabled());
204 documentImpl.setConstraintsEnabled(handlerFactory.getConstraintsEnabled());
205 }
206 documentImpl.marshalling();
207
208
209 String fileName = path;
210 if (!fileName.endsWith(this.documentType))
211 {
212 fileName = path + this.documentType;
213 }
214 File f = new File(this.documentRootDir, fileName);
215 Writer writer = null;
216
217 try
218 {
219
220
221
222
223 writer = new OutputStreamWriter(new FileOutputStream(f), PSML_DOCUMENT_ENCODING);
224 Serializer serializer = new XMLSerializer(writer, this.format);
225 final ContentHandler handler = serializer.asContentHandler();
226
227 Marshaller marshaller = new Marshaller(new ContentHandler()
228 {
229 private int menuDepth = 0;
230
231 public void characters(char[] ch, int start, int length) throws SAXException
232 {
233 handler.characters(ch, start, length);
234 }
235
236 public void endDocument() throws SAXException
237 {
238 handler.endDocument();
239 }
240
241 public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException
242 {
243 handler.ignorableWhitespace(ch, start, length);
244 }
245
246 public void processingInstruction(String target, String data) throws SAXException
247 {
248 handler.processingInstruction(target, data);
249 }
250
251 public void setDocumentLocator(Locator locator)
252 {
253 handler.setDocumentLocator(locator);
254 }
255
256 public void startDocument() throws SAXException
257 {
258 handler.startDocument();
259 }
260
261 public void endElement(String uri, String localName, String qName) throws SAXException {
262
263 if (qName.equals("menu"))
264 {
265 menuDepth--;
266 }
267
268
269 if ((menuDepth == 0) || !qName.equals("menu-element"))
270 {
271 handler.endElement(uri, localName, qName);
272 }
273 }
274
275 public void endPrefixMapping(String prefix) throws SAXException {
276 }
277
278 public void skippedEntity(String name) throws SAXException {
279 handler.skippedEntity(name);
280 }
281
282 public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
283
284 if ((menuDepth == 0) || !qName.equals("menu-element"))
285 {
286 handler.startElement(uri,localName, qName, atts);
287 }
288
289
290 if (qName.equals("menu"))
291 {
292 menuDepth++;
293 }
294 }
295
296 public void startPrefixMapping(String prefix, String uri) throws SAXException {
297 }
298 });
299 marshaller.setResolver((XMLClassDescriptorResolver) classDescriptorResolver);
300
301 marshaller.setValidation(false);
302 marshaller.marshal(document);
303 }
304 catch (MarshalException e)
305 {
306 log.error("Could not marshal the file " + f.getAbsolutePath(), e);
307 throw new FailedToUpdateDocumentException(e);
308 }
309 catch (ValidationException e)
310 {
311 log.error("Document " + f.getAbsolutePath() + " is not valid", e);
312 throw new FailedToUpdateDocumentException(e);
313 }
314 catch (IOException e)
315 {
316 log.error("Could not save the file " + f.getAbsolutePath(), e);
317 throw new FailedToUpdateDocumentException(e);
318 }
319 catch (Exception e)
320 {
321 log.error("Error while saving " + f.getAbsolutePath(), e);
322 throw new FailedToUpdateDocumentException(e);
323 }
324 finally
325 {
326 if (systemUpdate){
327
328 documentImpl.setPermissionsEnabled(handlerFactory.getPermissionsEnabled());
329 documentImpl.setConstraintsEnabled(handlerFactory.getConstraintsEnabled());
330 }
331 try
332 {
333 writer.close();
334 }
335 catch (IOException e)
336 {
337 }
338 }
339
340 }
341
342 protected void createCastorClassDescriptorResolver(String mappingFile) throws MappingException
343 {
344 Mapping mapping=null;
345 try
346 {
347 InputStream stream = getClass().getResourceAsStream(mappingFile);
348
349 if (log.isDebugEnabled())
350 {
351 log.debug("Loading psml mapping file " + mappingFile);
352 }
353
354 mapping = new Mapping();
355
356 InputSource is = new InputSource(stream);
357
358 is.setSystemId(mappingFile);
359 mapping.loadMapping(is);
360 }
361 catch (Exception e)
362 {
363 IllegalStateException ise = new IllegalStateException("Error in psml mapping creation");
364 ise.initCause(e);
365 throw ise;
366 }
367 this.classDescriptorResolver =
368 ClassDescriptorResolverFactory.createClassDescriptorResolver(BindingType.XML);
369 MappingUnmarshaller mappingUnmarshaller = new MappingUnmarshaller();
370 MappingLoader mappingLoader = mappingUnmarshaller.getMappingLoader(mapping, BindingType.XML);
371 classDescriptorResolver.setMappingLoader(mappingLoader);
372 }
373
374 protected Object unmarshallDocument( Class clazz, String path, String extension ) throws DocumentNotFoundException,
375 DocumentException
376 {
377 Document document = null;
378 File f = null;
379 if (path.endsWith(extension))
380 {
381 f = new File(this.documentRootDir, path);
382 }
383 else
384 {
385 f = new File(this.documentRootDir, path + extension);
386 }
387
388 if (!f.exists())
389 {
390 throw new PageNotFoundException("Document not found: " + path);
391 }
392
393 try
394 {
395
396
397
398
399
400 final InputSource readerInput = new InputSource(new InputStreamReader(new FileInputStream(f), PSML_DOCUMENT_ENCODING));
401 Unmarshaller unmarshaller = new Unmarshaller();
402 unmarshaller.setResolver((XMLClassDescriptorResolver) classDescriptorResolver);
403 unmarshaller.setValidation(false);
404 document = (Document) unmarshaller.unmarshal(new SAX2EventProducer()
405 {
406 public void setContentHandler(final ContentHandler handler)
407 {
408 xmlReader.setContentHandler(new ContentHandler()
409 {
410 private int menuDepth = 0;
411
412 public void characters(char[] ch, int start, int length) throws SAXException
413 {
414 handler.characters(ch, start, length);
415 }
416
417 public void endDocument() throws SAXException
418 {
419 handler.endDocument();
420 }
421
422 public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException
423 {
424 handler.ignorableWhitespace(ch, start, length);
425 }
426
427 public void processingInstruction(String target, String data) throws SAXException
428 {
429 handler.processingInstruction(target, data);
430 }
431
432 public void setDocumentLocator(Locator locator)
433 {
434 handler.setDocumentLocator(locator);
435 }
436
437 public void startDocument() throws SAXException
438 {
439 handler.startDocument();
440 }
441
442 public void endElement(String uri, String localName, String qName) throws SAXException {
443
444 handler.endElement(uri,localName,qName);
445
446
447
448 if (qName.equals("menu"))
449 {
450 menuDepth--;
451 if (menuDepth > 0)
452 {
453 handler.endElement(null,null,"menu-element");
454 }
455 }
456 else if ((menuDepth > 0) &&
457 (qName.equals("options") || qName.equals("separator") ||
458 qName.equals("include") || qName.equals("exclude")))
459 {
460 handler.endElement(null,null,"menu-element");
461 }
462 }
463
464 public void endPrefixMapping(String prefix) throws SAXException {
465 }
466
467 public void skippedEntity(String name) throws SAXException {
468 handler.skippedEntity(name);
469 }
470
471 public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
472
473
474
475
476 if (qName.equals("menu"))
477 {
478 if (menuDepth > 0)
479 {
480 handler.startElement(null,null,"menu-element", null);
481 }
482 menuDepth++;
483 }
484 else if ((menuDepth > 0) &&
485 (qName.equals("options") || qName.equals("separator") ||
486 qName.equals("include") || qName.equals("exclude")))
487 {
488 handler.startElement(null,null,"menu-element", null);
489 }
490
491
492 handler.startElement(null,null, qName, atts);
493 }
494
495 public void startPrefixMapping(String prefix, String uri) throws SAXException {
496 }
497 });
498 }
499 public void start() throws SAXException
500 {
501 try
502 {
503 xmlReader.parse(readerInput);
504 }
505 catch (IOException ioe)
506 {
507 throw new SAXException(ioe);
508 }
509 }
510 });
511
512 document.setPath(path);
513 AbstractBaseElement documentImpl = (AbstractBaseElement)document;
514 documentImpl.setHandlerFactory(handlerFactory);
515 documentImpl.setPermissionsEnabled(handlerFactory.getPermissionsEnabled());
516 documentImpl.setConstraintsEnabled(handlerFactory.getConstraintsEnabled());
517 documentImpl.unmarshalled();
518 if (document.isDirty()){
519 updateDocument(document, true);
520 document.setDirty(false);
521 }
522 }
523 catch (IOException e)
524 {
525 log.error("Could not load the file " + f.getAbsolutePath(), e);
526 throw new PageNotFoundException("Could not load the file " + f.getAbsolutePath(), e);
527 }
528 catch (MarshalException e)
529 {
530 log.error("Could not unmarshal the file " + f.getAbsolutePath(), e);
531 throw new PageNotFoundException("Could not unmarshal the file " + f.getAbsolutePath(), e);
532 }
533 catch (ValidationException e)
534 {
535 log.error("Document " + f.getAbsolutePath() + " is not valid", e);
536 throw new DocumentNotFoundException("Document " + f.getAbsolutePath() + " is not valid", e);
537 }
538
539
540 if (document == null)
541 {
542 throw new DocumentNotFoundException("Document not found: " + path);
543 }
544 else
545 {
546 if (!clazz.isAssignableFrom(document.getClass()))
547 {
548 throw new ClassCastException(document.getClass().getName() + " must implement or extend "
549 + clazz.getName());
550 }
551 return document;
552 }
553 }
554
555 protected void verifyPath( File path ) throws FileNotFoundException
556 {
557 if (path == null)
558 {
559 throw new IllegalArgumentException("Page root cannot be null");
560 }
561
562 if (!path.exists())
563 {
564 throw new FileNotFoundException("Could not locate root pages path " + path.getAbsolutePath());
565 }
566 }
567
568 /***
569 * <p>
570 * removeDocument
571 * </p>
572 *
573 * @see org.apache.jetspeed.page.document.DocumentHandler#removeDocument(org.apache.jetspeed.om.page.Document)
574 * @param document
575 * @throws DocumentNotFoundException
576 * @throws FailedToDeleteDocumentException
577 */
578 public void removeDocument( Document document ) throws DocumentNotFoundException, FailedToDeleteDocumentException
579 {
580
581 if (document == null)
582 {
583 log.warn("Recieved null Document to remove");
584 return;
585 }
586 String path = document.getPath();
587 if (path == null)
588 {
589 path = document.getId();
590 if (path == null)
591 {
592 log.warn("Recieved Document with null path/id to remove");
593 return;
594 }
595 }
596
597
598 String fileName = path;
599 if (!fileName.endsWith(this.documentType))
600 {
601 fileName = path + this.documentType;
602 }
603 File file = new File(this.documentRootDir, fileName);
604 if (!file.delete())
605 {
606 throw new FailedToDeleteDocumentException(file.getAbsolutePath()+" document cannot be deleted.");
607 }
608
609
610 fileCache.remove(path);
611
612
613 AbstractNode documentImpl = (AbstractNode)document;
614 documentImpl.setParent(null);
615 }
616
617 /***
618 * <p>
619 * getDocument
620 * </p>
621 *
622 * @see org.apache.jetspeed.page.document.DocumentHandler#getDocument(java.lang.String,
623 * boolean)
624 * @param name
625 * @param fromCahe
626 * Whether or not the Document should be pulled from the cache.
627 * @return @throws
628 * DocumentNotFoundException
629 */
630 public Document getDocument( String name, boolean fromCache ) throws DocumentNotFoundException, NodeException
631 {
632 Document document = null;
633 if (fromCache)
634 {
635 Object obj = fileCache.getDocument(name);
636 document = (Document) obj;
637 if (document == null)
638 {
639 document = (Document) unmarshallDocument(expectedReturnType, name, documentType);
640 addToCache(name, document);
641 }
642 }
643 else
644 {
645 document = (Document) unmarshallDocument(expectedReturnType, name, documentType);
646 }
647
648 return document;
649 }
650
651 /***
652 * <p>
653 * addToCache
654 * </p>
655 *
656 * @param path
657 * @param objectToCache
658 */
659 protected void addToCache( String path, Object objectToCache )
660 {
661 synchronized (fileCache)
662 {
663
664
665 try
666 {
667 fileCache.put(path, objectToCache, this.documentRootDir);
668
669 }
670 catch (java.io.IOException e)
671 {
672 log.error("Error putting document: " + e);
673 IllegalStateException ise = new IllegalStateException("Error storing Document in the FileCache: "
674 + e.toString());
675 ise.initCause(e);
676 }
677 }
678 }
679
680 /***
681 * <p>
682 * refresh
683 * </p>
684 *
685 * @see org.apache.jetspeed.cache.file.FileCacheEventListener#refresh(org.apache.jetspeed.cache.file.FileCacheEntry)
686 * @param entry
687 * @throws Exception
688 */
689 public void refresh( FileCacheEntry entry ) throws Exception
690 {
691 log.debug("Entry is refreshing: " + entry.getFile().getName());
692
693 if (entry.getDocument() instanceof Document && ((Document) entry.getDocument()).getPath().endsWith(documentType))
694 {
695 Document document = (Document) entry.getDocument();
696 Document freshDoc = getDocument(document.getPath(), false);
697 Node parent = ((AbstractNode)document).getParent(false);
698
699 freshDoc.setParent(parent);
700 if(parent instanceof FolderImpl)
701 {
702 FolderImpl folder = (FolderImpl) parent;
703 folder.getAllNodes().add(freshDoc);
704 }
705
706 freshDoc.setPath(document.getPath());
707 entry.setDocument(freshDoc);
708 }
709
710 }
711
712 /***
713 * <p>
714 * evict
715 * </p>
716 *
717 * @see org.apache.jetspeed.cache.file.FileCacheEventListener#evict(org.apache.jetspeed.cache.file.FileCacheEntry)
718 * @param entry
719 * @throws Exception
720 */
721 public void evict( FileCacheEntry entry ) throws Exception
722 {
723
724
725 }
726
727 /***
728 * <p>
729 * getType
730 * </p>
731 *
732 * @see org.apache.jetspeed.page.document.DocumentHandler#getType()
733 * @return
734 */
735 public String getType()
736 {
737 return documentType;
738 }
739
740 /***
741 * <p>
742 * getHandlerFactory
743 * </p>
744 *
745 * @see org.apache.jetspeed.page.document.DocumentHandler#getHandlerFactory()
746 * @return
747 */
748 public DocumentHandlerFactory getHandlerFactory()
749 {
750 return handlerFactory;
751 }
752
753 /***
754 * <p>
755 * setHandlerFactory
756 * </p>
757 *
758 * @see org.apache.jetspeed.page.document.DocumentHandler#setHandlerFactory(org.apache.jetspeed.page.document.DocumentHandlerFactory)
759 * @param factory
760 */
761 public void setHandlerFactory(DocumentHandlerFactory factory)
762 {
763 this.handlerFactory = factory;
764 }
765
766 }