All Downloads are FREE. Search and download functionalities are using the official Maven repository.

thredds.inventory.partition.DirectoryBuilder Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */
package thredds.inventory.partition;

import thredds.featurecollection.FeatureCollectionConfig;
import thredds.inventory.CollectionUpdateType;
import thredds.inventory.MCollection;
import thredds.inventory.MFile;
import ucar.nc2.util.Indent;

import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.Iterator;
import java.util.List;

/**
 * A Builder of DirectoryPartitions and DirectoryCollections.
 * Each DirectoryBuilder is associated with one directory, and one ncx index.
 * This may contain collections of files (MFiles in a DirectoryCollection), or subdirectories (MCollections in a DirectoryPartition).
 *
 * @author caron
 * @since 11/10/13
 */
public class DirectoryBuilder {

  // returns a DirectoryPartition or DirectoryCollection
  static public MCollection factory(FeatureCollectionConfig config, Path topDir, boolean isTop, IndexReader indexReader, String suffix, org.slf4j.Logger logger) throws IOException {
    DirectoryBuilder builder = new DirectoryBuilder(config.collectionName, topDir.toString(), suffix);

    DirectoryPartition dpart = new DirectoryPartition(config, topDir, isTop, indexReader, suffix, logger);
    if (!builder.isLeaf(indexReader))  { // its a partition
      return dpart;
    }

    // its a collection
    boolean hasIndex = builder.findIndex();
    if (hasIndex) {
      return dpart.makeChildCollection(builder);
    } else {
      DirectoryCollection result = new DirectoryCollection(config.collectionName, topDir, isTop, config.olderThan, logger); // no index file
      return result;
    }
  }

  private enum PartitionStatus {unknown, isDirectoryPartition, isLeaf}

  //////////////////////////////////////////////////////////////////////////////////////////////

  private static final boolean debug = false;
  private final String suffix;
  private final String topCollectionName;  // collection name
  private final String partitionName;      // partition name
  private final Path dir;                  // the directory
  private final FileTime dirLastModified;  // directory last modified
  private Path index;                      // TimePartition index file (ncx2 with magic = TimePartition)
  private FileTime indexLastModified;      // index last modified
  private long indexSize;                  // index size

  private boolean childrenConstructed = false;
  private List children = new ArrayList<>(25);
  private PartitionStatus partitionStatus = PartitionStatus.unknown;

  public DirectoryBuilder(String topCollectionName, String dirFilename, String suffix) throws IOException {
    this(topCollectionName, Paths.get(dirFilename), null, suffix);
  }

  /**
   * Create a DirectoryBuilder for the named directory
   * @param topCollectionName  from config, name of the collection
   * @param dir covers this directory
   * @param attr file attributes, may be null
   * @throws IOException
   */
  public DirectoryBuilder(String topCollectionName, Path dir, BasicFileAttributes attr, String suffix) throws IOException {
    this.topCollectionName = topCollectionName;
    this.dir = dir;
    this.partitionName = DirectoryCollection.makeCollectionName(topCollectionName, dir);
    this.suffix = suffix;

    if (attr == null)
      attr = Files.readAttributes(this.dir, BasicFileAttributes.class);
    if (!attr.isDirectory())
      throw new IllegalArgumentException("DirectoryPartitionBuilder needs a directory");
    dirLastModified = attr.lastModifiedTime();

    // see if we can find the index
    findIndex();
  }

  //public void setChildrenConstructed(boolean childrenConstructed) { this.childrenConstructed = childrenConstructed; }

  /**
   * Find the index file, using its canonical name
   * @return true if found
   * @throws IOException
   */
  public boolean findIndex() throws IOException {
    Path indexPath = Paths.get(dir.toString(), partitionName + suffix);
    if (Files.exists(indexPath)) {
      this.index = indexPath;
      BasicFileAttributes attr = Files.readAttributes(indexPath, BasicFileAttributes.class);
      this.indexLastModified = attr.lastModifiedTime();
      this.indexSize = attr.size();
      return true;
    }
    return false;
  }

  /**
   * Scans first 100 files to decide if its a leaf. If so, it becomes a DirectoryCollection, else a PartitionCollection.
   * @param indexReader reads the index
   * @return true if partition, false if file collection
   * @throws IOException on IO error
   */
  private boolean isLeaf(IndexReader indexReader) throws IOException {
    if (partitionStatus == PartitionStatus.unknown) {

        int countDir=0, countFile=0, count =0;
        try (DirectoryStream dirStream = Files.newDirectoryStream(dir)) {
          Iterator iterator = dirStream.iterator();
          while (iterator.hasNext() && count++ < 100) {
            Path p = iterator.next();
            BasicFileAttributes attr = Files.readAttributes(p, BasicFileAttributes.class);
            if (attr.isDirectory()) countDir++;
            else countFile++;
          }
        }
      partitionStatus = (countFile > countDir) ? PartitionStatus.isLeaf : PartitionStatus.isDirectoryPartition;
    }

    return partitionStatus == PartitionStatus.isLeaf;
  }

  /**
   * Find all children directories. Does not recurse.
   * We separate this from the constructor so it can be done on demand
   * Public for debugging.
   *
   * Look for children by:
   * 
    *
  1. If index exists , use the children inside there./li> *
  2. (or) scan the directory for children partitions
  3. *
* * @param indexReader this reads the index, and calls AddChild.addchild() for each child * @return children, may be empty but not null * @throws IOException */ public List constructChildren(IndexReader indexReader, CollectionUpdateType forceCollection) throws IOException { if (childrenConstructed) return children; if (index != null && forceCollection == CollectionUpdateType.nocheck) { // use index if it exists constructChildrenFromIndex(indexReader, false); } else { scanForChildren(); } //once we have found children, we know that this is a time partition partitionStatus = (children.size() > 0) ? PartitionStatus.isDirectoryPartition : PartitionStatus.isLeaf; childrenConstructed = true; // otherwise we are good return children; } public List constructChildrenFromIndex(IndexReader indexReader, boolean substituteParentDir) throws IOException { if (!indexReader.readChildren(index, new AddChildSub(substituteParentDir))) { partitionStatus = PartitionStatus.isLeaf; } return children; } // add a child partition from the index file (callback from constructChildren) // we dont know at this point if its another partition or a gribCollection private class AddChild implements IndexReader.AddChildCallback { public void addChild(String dirName, String indexFilename, long lastModified) throws IOException { Path indexPath = Paths.get(indexFilename); DirectoryBuilder child = new DirectoryBuilder(topCollectionName, indexPath, lastModified, suffix); children.add(child); } } private class AddChildSub implements IndexReader.AddChildCallback { boolean substituteParentDir; AddChildSub(boolean substituteParentDir) { this.substituteParentDir = substituteParentDir; } public void addChild(String dirName, String indexFilename, long lastModified) throws IOException { Path indexPath = Paths.get(dirName, indexFilename); if (substituteParentDir) { Path parent = index.getParent(); indexPath = parent.resolve( indexFilename); } DirectoryBuilder child = new DirectoryBuilder(topCollectionName, indexPath, lastModified, suffix); children.add(child); } } // coming in from the index reader private DirectoryBuilder(String topCollectionName, Path indexFile, long indexLastModified, String suffix) throws IOException { this.topCollectionName = topCollectionName; if (Files.exists(indexFile)) { this.index = indexFile; this.indexLastModified = FileTime.fromMillis(indexLastModified); } this.dir = indexFile.getParent(); this.partitionName = DirectoryCollection.makeCollectionName(topCollectionName, dir); BasicFileAttributes attr = Files.readAttributes(this.dir, BasicFileAttributes.class); if (!attr.isDirectory()) throw new IllegalArgumentException("DirectoryPartition needs a directory"); dirLastModified = attr.lastModifiedTime(); this.suffix = suffix; } /** * Scan for subdirectories, make each into a DirectoryBuilder and add as a child */ private void scanForChildren() { if (debug) System.out.printf("DirectoryBuilder.scanForChildren on %s ", dir); int count = 0; try (DirectoryStream ds = Files.newDirectoryStream(dir)) { for (Path p : ds) { BasicFileAttributes attr = Files.readAttributes(p, BasicFileAttributes.class); if (attr.isDirectory()) { children.add(new DirectoryBuilder(topCollectionName, p, attr, suffix)); if (debug && (++count % 10 == 0)) System.out.printf("%d ", count); } } } catch (IOException e) { e.printStackTrace(); } if (debug) System.out.printf("done=%d%n", count); childrenConstructed = true; } ////////////////////////////////////////////////////////////////////////////////////// // read the list of files from the index public List readFilesFromIndex(IndexReader indexReader) throws IOException { List result = new ArrayList<>(100); if (index == null) return result; indexReader.readMFiles(index, result); return result; } //////////////////////////////////////////////////////// /** * The directory that the partition covers * @return directory */ public Path getDir() { return dir; } /** * The ncx2 file * @return ncx2 file path */ public Path getIndex() { return index; } /** * May be null if constructChildren() was not called * @return children directories */ public List getChildren() { return children; } public String getPartitionName() { return partitionName; } public void show(Formatter out) { out.format("Collection %s%n", partitionName); toString(out, new Indent(2)); out.format("%n%n"); } private void toString(Formatter out, Indent indent) { out.format("%sDir '%s' (%s) index '%s' (%s)%n", indent, dir, dirLastModified, index, indexLastModified); indent.incr(); for (DirectoryBuilder c : children) c.toString(out, indent); indent.decr(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy