/* * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is the Kowari Metadata Store. * * The Initial Developer of the Original Code is Plugged In Software Pty * Ltd (http://www.pisoftware.com, mailto:info@pisoftware.com). Portions * created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002 * Plugged In Software Pty Ltd. All Rights Reserved. * * Contributor(s): N/A. * * [NOTE: The text of this Exhibit A may differ slightly from the text * of the notices in the Source Code files of the Original Code. You * should use the text of this Exhibit A rather than the text found in the * Original Code Source Code for Your Modifications.] * */ package org.mulgara.resolver.filesystem; // Java 2 standard packages import java.io.*; import java.net.URI; import java.net.URISyntaxException; import java.util.*; // Third party packages import org.jrdf.graph.*; import org.jrdf.graph.mem.*; import org.jrdf.vocabulary.RDF; import org.apache.log4j.Logger; // Apache Log4J import org.jrdf.util.ClosableIterator; // JRDF // Locally written packages import org.mulgara.resolver.filesystem.exception.*; import org.mulgara.resolver.filesystem.util.*; /** * Manages the navigation, caching, retrieval and meta-data construction of file * system information. * * @created 2004-11-25 * * @author Mark Ludlow * * @version $Revision: 1.8 $ * * @modified $Date: 2005/01/05 04:58:27 $ @maintenanceAuthor $Author: newmana $ * * @company <a href="mailto:info@PIsoftware.com">Plugged In Software</a> * * @copyright © 2004 <a href="http://www.PIsoftware.com/">Plugged In * Software Pty Ltd</a> * * @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a> */ public class MetaFileManager { /** Logger. */ private static final Logger log = Logger.getLogger(MetaFileManager.class); /** An interator into triples positioned at the next triple. */ private ClosableIterator<Triple> nextTriple; /** The model which will store the content of the current file metadata */ private Graph currentEntry; /** Pointer to the currently looked at directory */ private MetaFile currentDirectory; /** Pointer to the currently looked at child file */ private MetaFile currentChild; /** Pointer to the current root metafile */ private MetaFile root; /** The current included URI index we are looking at */ private int currentFileSystem; /** The list of excluded filesystems */ private ArrayList<File> exclusionList; /** The list of included filesystems */ private ArrayList<URI> inclusionList; /** * Flag to state whether we have completed our run of the inclusion list * or not */ private boolean complete; /** * Constructor. * * @param exclusions The list of excluded URIs from the included filesystems */ MetaFileManager(ArrayList<File> exclusions) { if (exclusions == null) { // If the exclusions list was null then use an empty list exclusions = new ArrayList<File>(); } // Store the excluded URIs exclusionList = exclusions; // Initialise the included file system list inclusionList = new ArrayList<URI>(); } /** * Adds a filesystem to be included in the manager. * * @param filesystem The filesystem to be included */ public void addFileSystem(URI filesystem) { // Add the filesystem to the manager inclusionList.add(filesystem); } /** * Readies the MetaFileManager for navigation of the filesystems. * * @throws FileManagerException */ public void prepare() throws FileManagerException { // Reset all tracking variables currentEntry = null; currentDirectory = null; currentChild = null; complete = false; currentFileSystem = -1; if (inclusionList.size() > 0) { // We need to check if we have any included files otherwise we can ignore // all processing try { // Initialise the graph for this file currentEntry = new GraphImpl(); } catch (GraphException graphException) { throw new FileManagerException("Failed to create graph during prepare " + "phase", graphException); } // Initialise the first file system complete = loadNextFileSystem(); } else { // We have no list to process so mark the processing as complete complete = true; } } /** * Readies the next file system in the list to be read from and returns * whether there was another filesystem or the end of the list is reached. * * @return Whether there are more filesystems * * @throws FileManagerException */ private boolean loadNextFileSystem() throws FileManagerException { // Increment the filesystem pointer currentFileSystem++; try { // Remove all statements in the graph (Normal methods of removal are // currently broken) currentEntry = new GraphImpl(); } catch (GraphException graphException) { throw new FileManagerException("Failed to clear graph before loading " + "next file system"); } // Container for our next filesystem's URI URI nextURI = null; try { // Retrieve the next filesystem URI from the inclusion list nextURI = inclusionList.get(currentFileSystem); } catch (IndexOutOfBoundsException outOfBoundsException) { // An invalid index means we have no more entries return true; } // Flag to indicate whether we should skip the exclusion test due to a bad // protocol which needs to be reported boolean badProtocol = !nextURI.getScheme().equals("file"); // Cycle through the list of inclusions until we either reach the end or // find a non-excluded value (This may include bad protocol entries) while (!badProtocol && exclusionList.contains(new File(nextURI))) { // Increment our counter currentFileSystem++; try { // Retrieve the next filesystem URI from the inclusion list nextURI = inclusionList.get(currentFileSystem); } catch (IndexOutOfBoundsException outOfBoundsException) { // An invalid index means we have no more entries return true; } // Check the protocol badProtocol = !nextURI.getScheme().equals("file"); } // Flag to indicate root creation success (Assumed to succeed) boolean couldCreate = true; try { // Create the root metafile root = new MetaFile(nextURI); } catch (IllegalArgumentException illegalArgumentException) { // Obtain the element factory GraphElementFactory elementFactory = currentEntry.getElementFactory(); // Containers for our subject, predicate, and object nodes SubjectNode subject = null; PredicateNode predicate = null; ObjectNode object = null; try { // Create the subject using the file URI subject = (SubjectNode) elementFactory.createResource(nextURI); } catch (GraphElementFactoryException graphElementFactoryException) { throw new FileManagerException("Failed to create subject node for " + nextURI.toString(), graphElementFactoryException); } try { // Create the RDF type predicate predicate = GraphUtil.createPredicateFromURI(RDF.TYPE, elementFactory); } catch (MetaDataException metaDataException) { throw new FileManagerException("Failed to create predicate node for " + RDF.TYPE, metaDataException); } try { // The default type is file object = (ObjectNode) elementFactory.createResource( new URI("http://mulgara.org/mulgara#InvalidFileSystem")); } catch (GraphElementFactoryException graphElementFactoryException) { throw new FileManagerException( "Failed to create object node for invalid file " + "system node " + "<http://mulgara.org/mulgara#InvalidFileSystem>", graphElementFactoryException); } catch (URISyntaxException uriSyntaxException) { throw new FileManagerException( "Failed to create object uri for invalid file " + "system node " + "<http://mulgara.org/mulgara#InvalidFileSystem>", uriSyntaxException); } try { // If the file uri was bad then add a triple to indicate this GraphUtil.addTriple(subject, predicate, object, currentEntry); } catch (MetaDataException metaDataException) { throw new FileManagerException("Failed to add triple [" + subject + ", " + predicate + ", " + object + "]", metaDataException); } // If the file cannot be created because the URI is invalid then fail // the creation couldCreate = false; } catch (MetaIOException metaIOException) { // Obtain the element factory GraphElementFactory elementFactory = currentEntry.getElementFactory(); // Containers for our subject, predicate, and object nodes SubjectNode subject = null; PredicateNode predicate = null; ObjectNode object = null; try { // Create the subject using the file URI subject = (SubjectNode) elementFactory.createResource(nextURI); } catch (GraphElementFactoryException graphElementFactoryException) { throw new FileManagerException("Failed to create subject node for " + nextURI.toString(), graphElementFactoryException); } try { // Create the RDF type predicate predicate = GraphUtil.createPredicateFromURI(RDF.TYPE, elementFactory); } catch (MetaDataException metaDataException) { throw new FileManagerException("Failed to create predicate node for " + RDF.TYPE, metaDataException); } try { // The default type is file object = (ObjectNode) elementFactory.createResource( new URI("http://mulgara.org/mulgara#InvalidFileSystem")); } catch (GraphElementFactoryException graphElementFactoryException) { throw new FileManagerException( "Failed to create object node for invalid file " + "system node " + "<http://mulgara.org/mulgara#FailedFileSystem>", graphElementFactoryException); } catch (URISyntaxException uriSyntaxException) { throw new FileManagerException( "Failed to create object uri for invalid file " + "system node " + "<http://mulgara.org/mulgara#FailedFileSystem>", uriSyntaxException); } try { // If the graph was unable to be created for the metafile then state that we // have a failed filesystem GraphUtil.addTriple(subject, predicate, object, currentEntry); } catch (MetaDataException metaDataException) { throw new FileManagerException("Failed to add triple [" + subject + ", " + predicate + ", " + object + "]", metaDataException); } // If the graph was unable to be created for the metafile then ignore it // as we cannot use the metafile object couldCreate = false; } if (couldCreate && !root.exists()) { // Obtain the element factory GraphElementFactory elementFactory = currentEntry.getElementFactory(); // Containers for our subject, predicate, and object nodes SubjectNode subject = null; PredicateNode predicate = null; ObjectNode object = null; try { // Create the subject using the file URI subject = (SubjectNode) elementFactory.createResource(nextURI); } catch (GraphElementFactoryException graphElementFactoryException) { throw new FileManagerException("Failed to create subject node for " + nextURI.toString(), graphElementFactoryException); } try { // Create the RDF type predicate predicate = GraphUtil.createPredicateFromURI(RDF.TYPE, elementFactory); } catch (MetaDataException metaDataException) { throw new FileManagerException("Failed to create predicate node for " + RDF.TYPE, metaDataException); } try { // The default type is file object = (ObjectNode) elementFactory.createResource( new URI("http://mulgara.org/mulgara#NonExistantFileSystem")); } catch (GraphElementFactoryException graphElementFactoryException) { throw new FileManagerException( "Failed to create object node for invalid file " + "system node " + "<http://mulgara.org/mulgara#NonExistantFileSystem>", graphElementFactoryException); } catch (URISyntaxException uriSyntaxException) { throw new FileManagerException( "Failed to create object uri for invalid file " + "system node " + "<http://mulgara.org/mulgara#NonExistantFileSystem>", uriSyntaxException); } try { // If the file uri was bad then add a triple to indicate this before // going on to the next entry GraphUtil.addTriple(subject, predicate, object, currentEntry); } catch (MetaDataException metaDataException) { throw new FileManagerException("Failed to add triple [" + subject + ", " + predicate + ", " + object + "]", metaDataException); } // If the file cannot be created because the URI is invalid then skip // processing it couldCreate = false; } if (couldCreate) { // Find the starting point of the traversal from the first inclusion Uri findStartPoint(root); try { // Obtain the current child's metadata currentEntry = currentChild.getMetadata(); } catch (MetaDataException metaDataException) { throw new FileManagerException("Unable to retrieve metadata for " + currentChild.toURI(), metaDataException); } catch (MetaIOException metaDataException) { throw new FileManagerException("Unable to perform metadata IO for " + currentChild.toURI(), metaDataException); } } else { // Set the current child to be the root file so we will skip it because // something has gone wrong during the startpoint process currentChild = root; } try { // Obtain the iterator for the graph nextTriple = currentEntry.find(null, null, null); } catch (GraphException graphException) { throw new FileManagerException("Unable to obtain triple iterator for " + currentChild.toURI(), graphException); } return false; } /** * Obtains the next triple in the current filesystem. This will come from the * graph of the current file's metadata, however, if the current file has been * completed then the next will be found and asked for its metadata. * * @return The next triple in the filesystem's metadata * * @throws FileManagerException */ public Triple nextTriple() throws FileManagerException { if (!complete) { // Only obtain triples if we are not completed if (!nextTriple.hasNext()) { if (currentChild != null && currentChild.equals(root)) { if (log.isDebugEnabled()) { log.debug("Finished processing filesystem [" + root.toURI() + "]"); } // Ready the system for the next entry if we have reached the root // entry for this inclusion complete = loadNextFileSystem(); if (complete) { // If this was the last filesystem then stop processing return null; } } // Advance the current child by one currentChild = currentChild.nextSibling(); if (!cycleToNextFile()) { // If there are no children left in the directory then obtain the next // directory's children getNextDirectory(); } else { try { // Obtain the new child's metadata currentEntry = currentChild.getMetadata(); } catch (MetaDataException metaDataException) { throw new FileManagerException("Unable to retrieve metadata for " + currentChild.toURI(), metaDataException); } catch (MetaIOException metaDataException) { throw new FileManagerException("Unable to perform metadata IO for " + currentChild.toURI(), metaDataException); } try { // Obtain the iterator for the graph nextTriple = currentEntry.find(null, null, null); } catch (GraphException graphException) { throw new FileManagerException( "Unable to obtain triple iterator for " + currentChild.toURI(), graphException); } } } return nextTriple.next(); } return null; } /** * Does a depth first traversal of the given metafile tree to obtain the * starting point for data collection. * * @param file The file that we wish to find the starting point to * * @throws FileManagerException */ private void findStartPoint(MetaFile file) throws FileManagerException { if (file.isDirectory()) { // We can only find the start point of a directory. If it is a file then // the start point is obviously the file. // Container for our directory list MetaFile[] directories = new MetaFile[0]; try { // Obtain a list of the directories in the root directories = file.listChildrenByFilter(new DirectoryFilter()); } catch (MetaIOException metaIOException) { throw new FileManagerException("Failed to obtain directory list for " + file.toURI(), metaIOException); } // Start the currentDirectory at the given file currentDirectory = file; // Descend through the first directory of each sub-directory until we reach // a directory with no children. while (directories.length > 0) { // Container for our directory index marker int i = 0; // Iterate through the list of directories and find the first // non-excluded directory for (i = 0; i < directories.length; i++) { if (!exclusionList.contains(directories[i])) { // Prevent further checking as we've found our first non-excluded // directory break; } } if (i < directories.length) { // If we found a non-excluded file then store it as the new current // directory currentDirectory = directories[i]; } else { // If we couldn't find any non-excluded files then do not proceed any // further break; } try { // Obtain the directories of the current directory directories = currentDirectory.listChildrenByFilter(new DirectoryFilter()); } catch (MetaIOException metaIOException) { throw new FileManagerException("Failed to obtain children list for " + directories[0].toURI(), metaIOException); } } // Container for our child list MetaFile[] children = new MetaFile[0]; try { // Obtain the list of children for the current directory children = currentDirectory.listChildren(); } catch (MetaIOException metaIOException) { throw new FileManagerException("Failed to obtain directory list for " + file.toURI(), metaIOException); } if (children.length > 0) { // Set the current file to be the beginning of the child list for the // current directory if we have children currentChild = children[0]; } } else { // Since we are using a file then just set it to be the current child currentChild = file; } } /** * Retrieve the next directory in the list of included directories and obtain * the children for it. The metadata for the current directory should be * stored before moving on else it will be skipped. * * @throws FileManagerException */ private void getNextDirectory() throws FileManagerException { if (currentDirectory.equals(root)) { if (log.isDebugEnabled()) { log.debug("Finalising processing of filesystem [" + root.toURI() + "]"); } // Before switching to the next inclusion, get the metadata for the root // file currentChild = root; try { // Obtain the current child's metadata currentEntry = currentChild.getMetadata(); } catch (MetaDataException metaDataException) { throw new FileManagerException("Failed to obtain metadata for " + currentChild.toURI(), metaDataException); } catch (MetaIOException metaIOException) { throw new FileManagerException("Failed to perform meta data io for " + currentChild.toURI(), metaIOException); } try { // Obtain the iterator for the graph nextTriple = currentEntry.find(null, null, null); } catch (GraphException graphException) { throw new FileManagerException( "Failed to obtain triple iterator for " + currentChild.toURI(), graphException); } } else { // Obtain the parent file to the current directory File parentFile = currentDirectory.getParentFile(); // Advance the current directory by one currentDirectory = currentDirectory.nextSibling(); if (!cycleToNextDirectory()) { // Container for our parent metafile MetaFile parent = null; try { // Get the parent of this directory as a directory parent = new MetaFile(parentFile.toURI()); } catch (MetaIOException metaIOException) { throw new FileManagerException("Failed to create parent metafile" + "of " + parentFile.toURI(), metaIOException); } if (!parent.equals(root)) { if (log.isDebugEnabled()) { log.debug("Finding parent file of: " + parent.getPath() + " from root " + root.getPath()); } // Check that we haven't traversed the last included directory and // find out where the parent directory falls within the structure of // its directory // Container for the super parent metafile MetaFile superParent = null; try { // Find the parent of the parent superParent = new MetaFile(parent.getParentFile().toURI()); } catch (MetaIOException metaIOException) { throw new FileManagerException( "Failed to create super parent metafile" + "for " + parent.toURI(), metaIOException); } // Container for our directory list MetaFile[] directories = new MetaFile[0]; try { // Find the directories of the super parent directories = superParent.listChildrenByFilter(new DirectoryFilter()); } catch (MetaIOException metaIOException) { throw new FileManagerException( "Failed to obtain directory list for " + superParent.toURI(), metaIOException); } // Convert the array to a list List<MetaFile> directoryList = Arrays.asList(directories); // Obtain the ordered and linked parent metafile from the list parent = directoryList.get(directoryList.indexOf(parent)); // Set the current directory to be the parent currentDirectory = parent; } else { // Set the current directory to be the root if we are back at the root currentDirectory = root; } } else { // Initialise the search using the new directory findStartPoint(currentDirectory); } // Container for the list of children MetaFile[] children = new MetaFile[0]; try { // Obtain the list of children for the current directory children = currentDirectory.listChildren(); } catch (MetaIOException metaIOException) { throw new FileManagerException("Failed to obtain children list for " + currentDirectory.toURI(), metaIOException); } if (children.length > 0) { // Set the current file to be the beginning of the child list for the // current directory if we have children currentChild = children[0]; if (cycleToNextFile()) { try { // Obtain the current child's metadata currentEntry = currentChild.getMetadata(); } catch (MetaDataException metaDataException) { throw new FileManagerException("Failed to obtain metadata for " + currentChild.toURI(), metaDataException); } catch (MetaIOException metaIOException) { throw new FileManagerException( "Failed to perform meta data io for " + currentChild.toURI(), metaIOException); } try { // Obtain the iterator for the graph nextTriple = currentEntry.find(null, null, null); } catch (GraphException graphException) { throw new FileManagerException( "Failed to obtain triple iterator for " + currentChild.toURI(), graphException); } } else { // If we found no included children in the directory then get the // next directory getNextDirectory(); } } else { // If the directory is empty then skip it and try again getNextDirectory(); } } } /** * Cycles through the currentChild list until either the next non-excluded * file is found or the end of the list reached. The boolean returned * indicates whether a sibling was found (true) or the end of the list was * reached. (false) * * @return Whether a sibling was found or the end of the list was reached */ private boolean cycleToNextFile() { // Cycle through the list of siblings until we find either the end of the // list or a non-excluded child while (currentChild != null && exclusionList.contains(currentChild)) { // Get the next child in the list currentChild = currentChild.nextSibling(); } if (currentChild == null) { // If we have a null value then we reached the end of the list so report // false return false; } return true; } /** * Cycles through the currentDirectory list until either the next non-excluded * directory is found or the end of the list reached. The boolean returned * indicates whether a sibling was found (true) or the end of the list was * reached. (false) * * @return Whether a sibling was found or the end of the list was reached */ private boolean cycleToNextDirectory() { // Cycle through the list of siblings until we find either the end of the // list or a non-excluded directory while (currentDirectory != null && exclusionList.contains(currentDirectory)) { // Get the next child in the list currentDirectory = currentDirectory.nextSibling(); } if (currentDirectory == null) { // If we have a null value then we reached the end of the list so report // false return false; } return true; } }