001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.jetspeed.page.document.psml;
018:
019: import java.io.File;
020: import java.io.FileNotFoundException;
021: import java.io.FilenameFilter;
022: import java.util.ArrayList;
023: import java.util.Iterator;
024: import java.util.List;
025:
026: import org.apache.commons.logging.Log;
027: import org.apache.commons.logging.LogFactory;
028: import org.apache.jetspeed.cache.file.FileCache;
029: import org.apache.jetspeed.cache.file.FileCacheEntry;
030: import org.apache.jetspeed.cache.file.FileCacheEventListener;
031: import org.apache.jetspeed.om.folder.Folder;
032: import org.apache.jetspeed.om.folder.FolderNotFoundException;
033: import org.apache.jetspeed.om.folder.InvalidFolderException;
034: import org.apache.jetspeed.om.folder.Reset;
035: import org.apache.jetspeed.om.folder.psml.FolderImpl;
036: import org.apache.jetspeed.om.folder.psml.FolderMetaDataImpl;
037: import org.apache.jetspeed.om.page.Document;
038:
039: import org.apache.jetspeed.page.document.DocumentHandler;
040: import org.apache.jetspeed.page.document.DocumentHandlerFactory;
041: import org.apache.jetspeed.page.document.DocumentNotFoundException;
042: import org.apache.jetspeed.page.document.FailedToDeleteFolderException;
043: import org.apache.jetspeed.page.document.FailedToUpdateFolderException;
044: import org.apache.jetspeed.page.document.FolderHandler;
045: import org.apache.jetspeed.page.document.Node;
046: import org.apache.jetspeed.page.document.NodeException;
047: import org.apache.jetspeed.page.document.NodeSet;
048: import org.apache.jetspeed.page.document.UnsupportedDocumentTypeException;
049:
050: /**
051: * <p>
052: * FileSystemFolderHandler
053: * </p>
054: * <p>
055: * Implementation of <code>FolderHanlder</code> that is based off of the file
056: * system.
057: * </p>
058: *
059: * @author <a href="mailto:weaver@apache.org">Scott T. Weaver </a>
060: * @version $Id: FileSystemFolderHandler.java 553584 2007-07-05 18:09:45Z taylor $
061: *
062: */
063: public class FileSystemFolderHandler implements FolderHandler,
064: FileCacheEventListener {
065:
066: private File documentRootDir;
067: private DocumentHandler metadataDocHandler;
068: private DocumentHandlerFactory handlerFactory;
069:
070: private final static Log log = LogFactory
071: .getLog(FileSystemFolderHandler.class);
072:
073: protected static final FilenameFilter FOLDER_FILTER = new FilenameFilter() {
074:
075: public boolean accept(File pathname, String fileName) {
076: return new File(pathname, fileName).isDirectory();
077: }
078:
079: };
080: private FileCache fileCache;
081:
082: /**
083: *
084: * @param documentRoot
085: * directory on file system to use as the root when locating
086: * folders
087: * @param handlerFactory
088: * A <code>DocumentHandlerFactory</code>
089: * @param fileCache
090: * For caching folder instances
091: * @throws FileNotFoundException
092: * if the <code>documentRoot</code> does not exist
093: * @throws UnsupportedDocumentTypeException
094: * if no <code>DocumentHnadler</code> could be found that
095: * supports folder metadata (folder.metadata) in the
096: * <code>handlerFactory</code>.
097: */
098: public FileSystemFolderHandler(String documentRoot,
099: DocumentHandlerFactory handlerFactory, FileCache fileCache)
100: throws FileNotFoundException,
101: UnsupportedDocumentTypeException {
102: super ();
103: this .documentRootDir = new File(documentRoot);
104: verifyPath(documentRootDir);
105: this .handlerFactory = handlerFactory;
106: this .metadataDocHandler = handlerFactory
107: .getDocumentHandler(FolderMetaDataImpl.DOCUMENT_TYPE);
108: this .fileCache = fileCache;
109: this .fileCache.addListener(this );
110: }
111:
112: /**
113: * <p>
114: * getFolder
115: * </p>
116: *
117: * @see org.apache.jetspeed.page.document.FolderHandler#getFolder(java.lang.String)
118: * @param path
119: * @return @throws
120: * FolderNotFoundException
121: * @throws FolderNotFoundException
122: * @throws InvalidFolderException
123: * @throws NodeException
124: * @throws DocumentNotFoundException
125: */
126: public Folder getFolder(String path)
127: throws FolderNotFoundException, InvalidFolderException,
128: NodeException {
129:
130: return getFolder(path, true);
131: }
132:
133: protected void verifyPath(File path) throws FileNotFoundException {
134: if (path == null) {
135: throw new IllegalArgumentException(
136: "Page root cannot be null");
137: }
138:
139: if (!path.exists()) {
140: throw new FileNotFoundException(
141: "Could not locate root pages path "
142: + path.getAbsolutePath());
143: }
144: }
145:
146: /**
147: * <p>
148: * getFolder
149: * </p>
150: *
151: * @see org.apache.jetspeed.page.document.FolderHandler#getFolder(java.lang.String,
152: * boolean)
153: * @param path
154: * @param fromCache
155: * @return @throws
156: * DocumentException, FolderNotFoundException
157: * @throws InvalidFolderException
158: * @throws DocumentNotFoundException
159: */
160: public Folder getFolder(String path, boolean fromCache)
161: throws NodeException, FolderNotFoundException,
162: InvalidFolderException {
163: Folder folder = null;
164: File folderFile = new File(documentRootDir, path);
165: if (!folderFile.exists()) {
166: throw new FolderNotFoundException(folderFile
167: .getAbsolutePath()
168: + " does not exist.");
169: }
170:
171: if (!folderFile.isDirectory()) {
172: throw new InvalidFolderException(folderFile
173: .getAbsolutePath()
174: + " is not a valid directory.");
175: }
176:
177: // cleanup trailing separators
178: if (!path.equals(Folder.PATH_SEPARATOR)
179: && path.endsWith(Folder.PATH_SEPARATOR)) {
180: path = path.substring(0, path.length() - 1);
181: }
182:
183: // check cache
184: if (fromCache) {
185: folder = (Folder) fileCache.getDocument(path);
186: }
187:
188: // get new folder
189: if (folder == null) {
190: try {
191: // look for metadata
192: FolderMetaDataImpl metadata = (FolderMetaDataImpl) metadataDocHandler
193: .getDocument(path + Folder.PATH_SEPARATOR
194: + FolderMetaDataImpl.DOCUMENT_TYPE);
195: folder = new FolderImpl(path, metadata, handlerFactory,
196: this );
197: } catch (DocumentNotFoundException e) {
198: // no metadata
199: folder = new FolderImpl(path, handlerFactory, this );
200: }
201:
202: // recursively set parent
203: if (!path.equals(Folder.PATH_SEPARATOR)) {
204: String parentPath = path;
205: int parentSeparatorIndex = parentPath
206: .lastIndexOf(Folder.PATH_SEPARATOR_CHAR);
207: if (parentSeparatorIndex > 0) {
208: parentPath = parentPath.substring(0,
209: parentSeparatorIndex);
210: } else {
211: parentPath = Folder.PATH_SEPARATOR;
212: }
213: folder.setParent(getFolder(parentPath));
214: }
215:
216: // folder unmarshalled
217: ((FolderImpl) folder).unmarshalled();
218:
219: // add to cache
220: if (fromCache) {
221: addToCache(path, folder);
222: }
223: }
224:
225: return folder;
226: }
227:
228: /**
229: * <p>
230: * updateFolder
231: * </p>
232: *
233: * @see org.apache.jetspeed.page.document.FolderHandler#updateFolder(org.apache.jetspeed.om.folder.Folder)
234: * @param folder
235: * @throws FailedToUpdateFolderException
236: */
237: public void updateFolder(Folder folder)
238: throws FailedToUpdateFolderException {
239: // sanity checks
240: if (folder == null) {
241: log.warn("Recieved null Folder to update");
242: return;
243: }
244: String path = folder.getPath();
245: if (path == null) {
246: path = folder.getId();
247: if (path == null) {
248: log.warn("Recieved Folder with null path/id to update");
249: return;
250: }
251: folder.setPath(path);
252: }
253:
254: // setup folder implementation
255: FolderImpl folderImpl = (FolderImpl) folder;
256: folderImpl.setFolderHandler(this );
257: folderImpl.setHandlerFactory(handlerFactory);
258: folderImpl.setPermissionsEnabled(handlerFactory
259: .getPermissionsEnabled());
260: folderImpl.setConstraintsEnabled(handlerFactory
261: .getConstraintsEnabled());
262: folderImpl.marshalling();
263:
264: // create underlying folder if it does not exist
265: File folderFile = new File(documentRootDir, path);
266: if ((folderFile.exists() && !folderFile.isDirectory())
267: || (!folderFile.exists() && !folderFile.mkdir())) {
268: throw new FailedToUpdateFolderException(folderFile
269: .getAbsolutePath()
270: + " does not exist and cannot be created.");
271: }
272:
273: // update metadata
274: try {
275: FolderMetaDataImpl metadata = folderImpl
276: .getFolderMetaData();
277: metadata.setPath(path + Folder.PATH_SEPARATOR
278: + FolderMetaDataImpl.DOCUMENT_TYPE);
279: metadataDocHandler.updateDocument(metadata);
280: } catch (Exception e) {
281: throw new FailedToUpdateFolderException(folderFile
282: .getAbsolutePath()
283: + " failed to update folder.metadata", e);
284: }
285:
286: // add to cache
287: addToCache(path, folder);
288: }
289:
290: /**
291: * <p>
292: * removeFolder
293: * </p>
294: *
295: * @see org.apache.jetspeed.page.document.FolderHandler#removeFolder(org.apache.jetspeed.om.folder.Folder)
296: * @param folder
297: * @throws FailedToDeleteFolderException
298: */
299: public void removeFolder(Folder folder)
300: throws FailedToDeleteFolderException {
301: // sanity checks
302: if (folder == null) {
303: log.warn("Recieved null Folder to remove");
304: return;
305: }
306: String path = folder.getPath();
307: if (path == null) {
308: path = folder.getId();
309: if (path == null) {
310: log.warn("Recieved Folder with null path/id to remove");
311: return;
312: }
313: folder.setPath(path);
314: }
315:
316: // remove folder nodes
317: FolderImpl folderImpl = (FolderImpl) folder;
318: try {
319: // copy all folder nodes to remove
320: List removeNodes = new ArrayList();
321: Iterator copyIter = folderImpl.getAllNodes().iterator();
322: while (copyIter.hasNext()) {
323: removeNodes.add(copyIter.next());
324: }
325:
326: // remove folder nodes
327: Iterator removeIter = removeNodes.iterator();
328: while (removeIter.hasNext()) {
329: Node node = (Node) removeIter.next();
330: if (node instanceof Folder) {
331: // recursively remove folder
332: removeFolder((Folder) node);
333: } else if (node instanceof Document) {
334: // remove folder document
335: try {
336: handlerFactory.getDocumentHandler(
337: node.getType()).removeDocument(
338: (Document) node);
339: } catch (Exception e) {
340: File documentFile = new File(
341: this .documentRootDir, node.getPath());
342: throw new FailedToDeleteFolderException(
343: documentFile.getAbsolutePath()
344: + " document cannot be deleted.");
345: }
346: }
347: ((NodeSetImpl) folderImpl.getAllNodes()).remove(node);
348: }
349: } catch (FailedToDeleteFolderException fdfe) {
350: throw fdfe;
351: } catch (Exception e) {
352: throw new FailedToDeleteFolderException(e.getMessage());
353: }
354:
355: // remove underlying folder and unknown files
356: File folderFile = new File(this .documentRootDir, path);
357: File metadataFile = null;
358: if ((folderImpl.getFolderMetaData() != null)
359: && (folderImpl.getFolderMetaData().getPath() != null)) {
360: metadataFile = new File(this .documentRootDir, folderImpl
361: .getFolderMetaData().getPath());
362: }
363: if (folderFile.exists() && folderFile.isDirectory()) {
364: // attempt to clean folder for delete
365: String[] contents = folderFile.list();
366: for (int i = 0; (i < contents.length); i++) {
367: File contentFile = new File(folderFile, contents[i]);
368: if ((metadataFile == null)
369: || !contentFile.equals(metadataFile)) {
370: if (!deleteFile(contentFile)) {
371: throw new FailedToDeleteFolderException(
372: folderFile.getAbsolutePath()
373: + " unrecognized folder contents cannot be deleted.");
374: }
375: }
376: }
377: // delete folder and metadata
378: if ((metadataFile != null) && metadataFile.exists()
379: && !metadataFile.delete()) {
380: throw new FailedToDeleteFolderException(folderFile
381: .getAbsolutePath()
382: + " folder metadata cannot be deleted.");
383: }
384: // delete folder and all remaining folder contents
385: // unless folder is root folder which should be
386: // preserved as PSML "mount point"
387: if (!path.equals(Folder.PATH_SEPARATOR)
388: && !folderFile.delete()) {
389: throw new FailedToDeleteFolderException(folderFile
390: .getAbsolutePath()
391: + " folder cannot be deleted.");
392: }
393: } else {
394: throw new FailedToDeleteFolderException(folderFile
395: .getAbsolutePath()
396: + " not found.");
397: }
398:
399: // remove from cache
400: fileCache.remove(path);
401:
402: // reset folder
403: if (folderImpl.getFolderMetaData() != null) {
404: folderImpl.getFolderMetaData().setParent(null);
405: }
406: folderImpl.setParent(null);
407: folderImpl.reset();
408: }
409:
410: private static final boolean deleteFile(File file) {
411: if (file.isDirectory()) {
412: String[] children = file.list();
413: for (int i = 0; (i < children.length); i++) {
414: if (!deleteFile(new File(file, children[i]))) {
415: return false;
416: }
417: }
418: }
419: return file.delete();
420: }
421:
422: /**
423: * <p>
424: * getFolders
425: * </p>
426: *
427: * @see org.apache.jetspeed.page.document.FolderHandler#getFolders(java.lang.String)
428: * @param path
429: * @return @throws
430: * FolderNotFoundException
431: * @throws FolderNotFoundException
432: * @throws InvalidFolderException
433: * @throws NodeException
434: */
435: public NodeSet getFolders(String path)
436: throws FolderNotFoundException, InvalidFolderException,
437: NodeException {
438: File parent = new File(documentRootDir, path);
439: if (!parent.exists()) {
440: throw new FolderNotFoundException(
441: "No folder exists at the path: "
442: + parent.getAbsolutePath());
443: } else {
444: String[] children = getChildrenNames(path, FOLDER_FILTER);
445: NodeSetImpl folders = new NodeSetImpl(path);
446: for (int i = 0; i < children.length; i++) {
447: if (path.endsWith(Folder.PATH_SEPARATOR)) {
448: folders.add(getFolder(path + children[i]));
449: } else {
450: folders.add(getFolder(path + Folder.PATH_SEPARATOR
451: + children[i]));
452: }
453: }
454: return folders;
455: }
456: }
457:
458: public class DocumentTypeFilter implements FilenameFilter {
459: private String documentType;
460:
461: public DocumentTypeFilter(String documentType) {
462: this .documentType = documentType;
463: }
464:
465: /**
466: * <p>
467: * accept
468: * </p>
469: *
470: * @see java.io.FilenameFilter#accept(java.io.File, java.lang.String)
471: * @param dir
472: * @param name
473: * @return
474: */
475: public boolean accept(File dir, String name) {
476: return name.endsWith(documentType);
477: }
478:
479: }
480:
481: /**
482: * <p>
483: * list
484: * </p>
485: *
486: * @see org.apache.jetspeed.page.document.FolderHandler#list(java.lang.String)
487: * @param documentType
488: * @return @throws
489: * FolderNotFoundException
490: */
491: public String[] list(String folderPath, String documentType)
492: throws FolderNotFoundException {
493: return getChildrenNames(folderPath, new DocumentTypeFilter(
494: documentType));
495: }
496:
497: /**
498: * <p>
499: * listAll
500: * </p>
501: *
502: * @see org.apache.jetspeed.page.document.FolderHandler#listAll(java.lang.String)
503: * @param folderPath
504: * @return @throws
505: * FolderNotFoundException
506: */
507: public String[] listAll(String folderPath)
508: throws FolderNotFoundException {
509: return getChildrenNames(folderPath, null);
510: }
511:
512: protected String[] getChildrenNames(String path,
513: FilenameFilter filter) throws FolderNotFoundException {
514: File parent = new File(documentRootDir, path);
515: if (!parent.exists()) {
516: throw new FolderNotFoundException(
517: "No folder exists at the path: "
518: + parent.getAbsolutePath());
519: } else {
520: if (filter != null) {
521: return parent.list(filter);
522: } else {
523: return parent.list();
524: }
525: }
526: }
527:
528: /**
529: * <p>
530: * getChildNodes
531: * </p>
532: *
533: * @see org.apache.jetspeed.page.document.FolderHandler#getNodes(java.lang.String,boolean,java.lang.String)
534: * @param path
535: * @param regexp
536: * @param documentType
537: * @return NodeSet
538: * @throws FolderNotFoundException
539: * @throws DocumentException
540: * @throws InvalidFolderException
541: * @throws NodeException
542: */
543: public NodeSet getNodes(String path, boolean regexp,
544: String documentType) throws FolderNotFoundException,
545: InvalidFolderException, NodeException {
546: // path must be valid absolute path
547: if ((path == null) || !path.startsWith(Folder.PATH_SEPARATOR)) {
548: throw new InvalidFolderException("Invalid path specified "
549: + path);
550: }
551:
552: // traverse folders and parse path from root,
553: // accumualting matches in node set
554: Folder folder = getFolder(Folder.PATH_SEPARATOR);
555: NodeSetImpl matched = new NodeSetImpl(null);
556: getNodes(folder, path, regexp, matched);
557:
558: // return matched nodes filtered by document type
559: if (documentType != null) {
560: return matched.subset(documentType);
561: }
562: return matched;
563: }
564:
565: private void getNodes(Folder folder, String path, boolean regexp,
566: NodeSet matched) throws FolderNotFoundException,
567: InvalidFolderException, NodeException {
568: // test for trivial folder match
569: if (path.equals(Folder.PATH_SEPARATOR)) {
570: matched.add(folder);
571: return;
572: }
573:
574: // remove leading separator
575: if (path.startsWith(Folder.PATH_SEPARATOR)) {
576: path = path.substring(1);
577: }
578:
579: // parse path for folder path match
580: int separatorIndex = path.indexOf(Folder.PATH_SEPARATOR);
581: if (separatorIndex != -1) {
582: // match folder name
583: String folderName = path.substring(0, separatorIndex);
584: String folderPath = (folder.getPath().endsWith(
585: Folder.PATH_SEPARATOR) ? folder.getPath() : folder
586: .getPath()
587: + Folder.PATH_SEPARATOR)
588: + folderName;
589: NodeSet matchedFolders = null;
590: if (regexp) {
591: // get regexp matched folders
592: matchedFolders = ((FolderImpl) folder)
593: .getFolders(false).inclusiveSubset(folderPath);
594: } else {
595: // get single matched folder
596: Folder matchedFolder = getFolder(folderPath);
597: if (matchedFolder != null) {
598: matchedFolders = new NodeSetImpl(folder.getPath());
599: matchedFolders.add(matchedFolder);
600: }
601: }
602: if ((matchedFolders == null)
603: || (matchedFolders.size() == 0)) {
604: throw new FolderNotFoundException("Cannot find folder"
605: + folderName + " in " + folder.getPath());
606: }
607:
608: // match recursively over matched folders
609: path = path.substring(separatorIndex);
610: Iterator matchedFoldersIter = matchedFolders.iterator();
611: while (matchedFoldersIter.hasNext()) {
612: Folder matchedFolder = (Folder) matchedFoldersIter
613: .next();
614: getNodes(matchedFolder, path, regexp, matched);
615: }
616: return;
617: }
618:
619: // match node name
620: String nodeName = path;
621: String nodePath = (folder.getPath().endsWith(
622: Folder.PATH_SEPARATOR) ? folder.getPath() : folder
623: .getPath()
624: + Folder.PATH_SEPARATOR)
625: + nodeName;
626: if (regexp) {
627: // get regexp matched nodes
628: Iterator addIter = ((FolderImpl) folder).getAllNodes()
629: .inclusiveSubset(nodePath).iterator();
630: while (addIter.hasNext()) {
631: matched.add((Node) addIter.next());
632: }
633: } else {
634: // get single matched node
635: Iterator findIter = ((FolderImpl) folder).getAllNodes()
636: .iterator();
637: while (findIter.hasNext()) {
638: Node addNode = (Node) findIter.next();
639: if (addNode.getPath().equals(nodePath)) {
640: matched.add(addNode);
641: break;
642: }
643: }
644: }
645: }
646:
647: /**
648: * <p>
649: * addToCache
650: * </p>
651: *
652: * @param id
653: * @param objectToCache
654: */
655: protected void addToCache(String id, Object objectToCache) {
656: synchronized (fileCache) {
657: // store the document in the hash and reference it to the
658: // watcher
659: try {
660: fileCache.put(id, objectToCache, this .documentRootDir);
661:
662: } catch (java.io.IOException e) {
663:
664: String msg = "Error storing Document in the FileCache: "
665: + e.toString();
666: log.error(msg);
667: IllegalStateException ise = new IllegalStateException(
668: msg);
669: ise.initCause(e);
670: }
671: }
672: }
673:
674: /**
675: * <p>
676: * refresh
677: * </p>
678: *
679: * @see org.apache.jetspeed.cache.file.FileCacheEventListener#refresh(org.apache.jetspeed.cache.file.FileCacheEntry)
680: * @param entry
681: * @throws Exception
682: */
683: public void refresh(FileCacheEntry entry) throws Exception {
684: if (entry.getDocument() instanceof Folder) {
685: Folder folder = (Folder) entry.getDocument();
686: entry.setDocument(getFolder(folder.getPath(), false));
687: if (((AbstractNode) folder).getParent(false) != null) {
688: FileCacheEntry parentEntry = fileCache
689: .get(((AbstractNode) folder).getParent(false)
690: .getPath());
691: refresh(parentEntry);
692: }
693: } else if (entry.getDocument() instanceof Document) {
694: Document doc = (Document) entry.getDocument();
695: if (doc.getType().equals(FolderMetaDataImpl.DOCUMENT_TYPE)) {
696: FileCacheEntry folderEntry = fileCache
697: .get(((AbstractNode) doc).getParent().getPath());
698: refresh(folderEntry);
699: }
700: }
701:
702: if (entry.getDocument() instanceof Reset) {
703: ((Reset) entry.getDocument()).reset();
704: }
705:
706: }
707:
708: /**
709: * <p>
710: * evict
711: * </p>
712: *
713: * @see org.apache.jetspeed.cache.file.FileCacheEventListener#evict(org.apache.jetspeed.cache.file.FileCacheEntry)
714: * @param entry
715: * @throws Exception
716: */
717: public void evict(FileCacheEntry entry) throws Exception {
718:
719: }
720:
721: public boolean isFolder(String path) {
722: return new File(this.documentRootDir, path).isDirectory();
723: }
724: }
|