Package v7db.files.mongodb

Source Code of v7db.files.mongodb.V7GridFS

/**
* Copyright (c) 2011-2012, Thilo Planz. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package v7db.files.mongodb;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.bson.BSONObject;
import org.bson.types.ObjectId;

import v7db.files.ContentStorageFacade;
import v7db.files.spi.Content;
import v7db.files.spi.ContentPointer;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.WriteConcern;
import com.mongodb.WriteResult;

public class V7GridFS {

  private final DBCollection files;

  private final ContentStorageFacade storage;

  public static final String COLLECTION_NAME_FILES = "v7files.files";

  public V7GridFS(DB db) {
    files = db.getCollection(COLLECTION_NAME_FILES);
    storage = new ContentStorageFacade(new MongoContentStorage(db),
        new MongoReferenceTracking(db));
  }

  public V7File getFile(String... path) {

    // the filesystem root
    V7File parentFile = V7File.lazy(this, path[0], null);

    if (path.length == 1) {
      return parentFile;
    }

    DBObject metaData;
    // directly under the root
    if (path.length == 2) {
      metaData = files.findOne(new BasicDBObject("parent", path[0])
          .append("filename", path[1]));
    }

    else {
      List<String> filenames = Arrays.asList(path)
          .subList(1, path.length);
      List<DBObject> candidates = files.find(
          new BasicDBObject("filename", new BasicDBObject("$in",
              filenames))).toArray();
      // we need to have at least one candidate for every path component
      if (candidates.size() < filenames.size())
        return null;

      Object parent = path[0];

      metaData = null;
      path: for (String fileName : filenames) {
        for (DBObject c : candidates) {
          if (parent.equals(c.get("parent"))
              && fileName.equals(c.get("filename"))) {
            parent = c.get("_id");
            parentFile = new V7File(this, metaData, parentFile);
            metaData = c;

            continue path;
          }
        }
        return null;
      }
    }

    if (metaData == null)
      return null;
    return new V7File(this, metaData, parentFile);
  }

  /**
   * @param data
   *            can be null, for a file without content (e.g. a folder)
   * @param parentFileId
   * @param filename
   * @return
   * @throws IOException
   */
  public ObjectId addFolder(Object parentFileId, String filename)
      throws IOException {
    return addFile(null, 0, 0, parentFileId, filename, null);
  }

  /**
   * @param data
   *            can be null, for a file without content (e.g. a folder)
   * @param parentFileId
   * @param filename
   * @return
   * @throws IOException
   */
  public ObjectId addFile(byte[] data, Object parentFileId, String filename,
      String contentType) throws IOException {
    if (data == null)
      return addFile(null, 0, 0, parentFileId, filename, contentType);
    return addFile(data, 0, data.length, parentFileId, filename,
        contentType);
  }

  public ObjectId addFile(ContentPointer data, Object parentFileId,
      String filename, String contentType) throws IOException {
    if (data == null)
      return addFile(null, 0, 0, parentFileId, filename, contentType);

    ObjectId fileId = new ObjectId();
    BasicDBObject metaData = new BasicDBObject("parent", parentFileId)
        .append("_id", fileId);

    metaData.putAll(storage.updateBackRefs(data, fileId, filename,
        contentType));

    insertMetaData(metaData);
    return fileId;
  }

  public ObjectId addFile(byte[] data, int offset, int len,
      Object parentFileId, String filename, String contentType)
      throws IOException {

    ObjectId fileId = new ObjectId();
    BasicDBObject metaData = new BasicDBObject("parent", parentFileId)
        .append("_id", fileId);

    metaData.putAll(storage.inlineOrInsertContentsAndBackRefs(100, data,
        offset, len, fileId, filename, contentType));

    insertMetaData(metaData);
    return fileId;
  }

  /**
   * will close the InputStream before returning
   */
  public ObjectId addFile(InputStream data, Object parentFileId,
      String filename, String contentType) throws IOException {
    ObjectId fileId = new ObjectId();
    BasicDBObject metaData = new BasicDBObject("parent", parentFileId)
        .append("_id", fileId);

    metaData.putAll(storage.insertContentsAndBackRefs(data, fileId,
        filename, contentType));

    insertMetaData(metaData);
    return fileId;
  }

  public List<V7File> getChildren(V7File parent) {
    List<V7File> children = new ArrayList<V7File>();
    for (DBObject child : files.find(new BasicDBObject("parent", parent
        .getId()))) {
      children.add(new V7File(this, child, parent));
    }
    return children;
  }

  private void insertMetaData(DBObject metaData) throws IOException {
    metaData.put("_version", 1);
    metaData.put("created_at", new Date());
    WriteResult result = files.insert(WriteConcern.SAFE, metaData);
    String error = result.getError();
    if (error != null)
      throw new IOException(error);
  }

  void updateMetaData(DBObject metaData) throws IOException {
    metaData.put("updated_at", new Date());
    try {
      Vermongo.update(files, metaData);
    } catch (UpdateConflictException e) {
      throw new IOException(e);
    }

  }

  void updateContents(DBObject metaData, byte[] contents) throws IOException {
    updateContents(metaData, contents, 0, contents == null ? 0
        : contents.length);
  }

  void updateContents(DBObject metaData, ContentPointer newContents)
      throws IOException {
    ContentPointer oldContents = getContentPointer(metaData);

    if (newContents.contentEquals(oldContents))
      return;

    String filename = (String) metaData.get("filename");
    String contentType = (String) metaData.get("contentType");
    Object fileId = metaData.get("_id");

    BSONObject newContent = storage.updateBackRefs(newContents, fileId,
        filename, contentType);

    metaData.removeField("sha");
    metaData.removeField("length");
    metaData.removeField("in");

    metaData.putAll(newContent);

    updateMetaData(metaData);
  }

  void insertContents(DBObject metaData, ContentPointer newContents)
      throws IOException {

    String filename = (String) metaData.get("filename");
    String contentType = (String) metaData.get("contentType");
    Object fileId = metaData.get("_id");

    if (newContents != null) {
      BSONObject newContent = storage.updateBackRefs(newContents, fileId,
          filename, contentType);

      metaData.removeField("sha");
      metaData.removeField("length");
      metaData.removeField("in");

      metaData.putAll(newContent);
    }

    insertMetaData(metaData);
  }

  /**
   * read into the buffer, continuing until the stream is finished or the
   * buffer is full.
   *
   * @return the number of bytes read, which could be 0 (not -1)
   * @throws IOException
   */
  static int readFully(InputStream data, byte[] buffer) throws IOException {
    int read = data.read(buffer);
    if (read == -1) {
      return 0;
    }
    while (read < buffer.length) {
      int added = data.read(buffer, read, buffer.length - read);
      if (added == -1)
        return read;
      read += added;
    }
    return read;
  }

  void updateContents(DBObject metaData, InputStream contents, Long size)
      throws IOException {
    if (contents == null) {
      updateContents(metaData, (byte[]) null);
      return;
    }
    if (size != null) {
      if (size <= 1024 * 1024) {
        updateContents(metaData, IOUtils.toByteArray(contents, size));
        return;
      }
    }

    updateContents(metaData, contents);
  }

  private void updateContents(DBObject metaData, InputStream contents)
      throws IOException {

    Object fileId = metaData.get("_id");
    ContentPointer oldContents = getContentPointer(metaData);
    String filename = (String) metaData.get("filename");
    String contentType = (String) metaData.get("contentType");

    BSONObject newContent = storage.insertContentsAndBackRefs(contents,
        fileId, filename, contentType);

    // check if it has changed
    ContentPointer newContents = getContentPointer(newContent);
    if (newContents.contentEquals(oldContents))
      return;

    metaData.removeField("sha");
    metaData.removeField("length");
    metaData.removeField("in");

    metaData.putAll(newContent);

    updateMetaData(metaData);
  }

  private void updateContents(DBObject metaData, byte[] contents, int offset,
      int len) throws IOException {

    Object fileId = metaData.get("_id");
    ContentPointer oldContents = getContentPointer(metaData);
    String filename = (String) metaData.get("filename");
    String contentType = (String) metaData.get("contentType");

    // for up to 55 bytes, storing the complete file inline
    // takes less space than just storing the SHA-1 and length
    // 20 (SHA-1) + 1 (sha - in) + 6 (length) + 4 (int32) + 2*12
    // (ObjectId back-references)
    BSONObject newContent = storage.inlineOrInsertContentsAndBackRefs(55,
        contents, offset, len, fileId, filename, contentType);

    // check if it has changed
    ContentPointer newContents = getContentPointer(newContent);
    if (newContents.contentEquals(oldContents))
      return;

    metaData.removeField("sha");
    metaData.removeField("length");
    metaData.removeField("in");

    metaData.putAll(newContent);

    updateMetaData(metaData);
  }

  public V7File getChild(V7File parentFile, String childName) {
    DBObject child = files.findOne(new BasicDBObject("parent", parentFile
        .getId()).append("filename", childName));
    if (child == null)
      return null;
    return new V7File(this, child, parentFile);
  }

  void delete(V7File file) throws IOException {
    // TODO: should check the version present in the db
    Vermongo.remove(files, file.getId(), new BasicDBObject("deleted_at",
        new Date()));
    storage.insertContentsAndBackRefs(null, file.getId(), null, null);
  }

  ContentPointer getContentPointer(BSONObject metaData) {
    return storage.getContentPointer(metaData);
  }

  Content getContent(BSONObject metaData) throws IOException {
    ContentPointer pointer = storage.getContentPointer(metaData);
    return storage.getContent(pointer);
  }

}
TOP

Related Classes of v7db.files.mongodb.V7GridFS

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.