Package org.olat.core.util.vfs.version

Source Code of org.olat.core.util.vfs.version.VersionsFileManager

/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) frentix GmbH<br>
* http://www.frentix.com<br>
* <p>
*/
package org.olat.core.util.vfs.version;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import org.olat.core.commons.modules.bc.FolderConfig;
import org.olat.core.commons.modules.bc.meta.MetaInfo;
import org.olat.core.commons.modules.bc.meta.tagged.MetaTagged;
import org.olat.core.id.Identity;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.vfs.LocalFolderImpl;
import org.olat.core.util.vfs.LocalImpl;
import org.olat.core.util.vfs.MergeSource;
import org.olat.core.util.vfs.NamedContainerImpl;
import org.olat.core.util.vfs.OlatRelPathImpl;
import org.olat.core.util.vfs.VFSConstants;
import org.olat.core.util.vfs.VFSContainer;
import org.olat.core.util.vfs.VFSItem;
import org.olat.core.util.vfs.VFSLeaf;
import org.olat.core.util.vfs.VFSManager;
import org.olat.core.util.vfs.filters.VFSItemSuffixFilter;
import org.olat.core.util.vfs.filters.VFSLeafFilter;
import org.olat.core.util.xml.XStreamHelper;

import com.thoughtworks.xstream.XStream;

/**
*
* Description:<br>
* This implementation of the VersionsManager saved the revisions of a file in a
* file with the same name as the original + ".xml". This xml file is saved in
* a parallel folder .version under the root defined in FolderConfig. Every revision'file
* have a name made of a generated unique id + the name of the original file.
*
* <P>
* Initial Date:  21 sept. 2009 <br>
*
* @author srosse
*/
public class VersionsFileManager extends VersionsManager {
  private static final OLog log = Tracing.createLoggerFor(VersionsFileManager.class);

  private static final Versions NOT_VERSIONED = new NotVersioned();
  private static final XStream mystream;
  static {
    mystream = XStreamHelper.createXStreamInstance();
    mystream.alias("versions", VersionsFileImpl.class);
    mystream.alias("revision", RevisionFileImpl.class);
    mystream.omitField(VersionsFileImpl.class, "currentVersion");
    mystream.omitField(VersionsFileImpl.class, "versionFile");
    mystream.omitField(RevisionFileImpl.class, "current");
    mystream.omitField(RevisionFileImpl.class, "container");
    mystream.omitField(RevisionFileImpl.class, "file");
  }

  private File rootVersionFolder;
  private VFSContainer rootVersionsContainer;

  public VersionsFileManager() {
  //
  }

  @Override
  public Versions createVersionsFor(VFSLeaf leaf) {
    if (!(leaf instanceof Versionable)) {
      return NOT_VERSIONED;
    } else if (isVersionFile(leaf)) {
      return NOT_VERSIONED;
    }

    Versions versions = readVersions(leaf, false);
    return versions;
  }

  @Override
  public List<Versions> getDeletedFiles(VFSContainer container) {
    List<Versions> deletedRevisions = new ArrayList<Versions>();

    VFSContainer versionContainer = getCanonicalVersionFolder(container, false);
    if (versionContainer != null) {
      Set<String> currentNames = new HashSet<String>();
      for (VFSItem item : container.getItems(new VFSLeafFilter())) {
        currentNames.add(item.getName() + ".xml");
      }

      List<VFSItem> versionItems = versionContainer.getItems(new VFSItemSuffixFilter(new String[] { "xml" }));
      for (VFSItem versionItem : versionItems) {
        if (versionItem instanceof VFSLeaf && !currentNames.contains(versionItem.getName())) {
          Versions versions = readVersions(null, (VFSLeaf) versionItem);
          List<VFSRevision> revisions = versions.getRevisions();
          if (!revisions.isEmpty()) {
            deletedRevisions.add(versions);
          }
        }
      }
    }
    return deletedRevisions;
  }

  private Versions readVersions(VFSLeaf leaf, boolean create) {
    VFSLeaf fVersions = getCanonicalVersionXmlFile(leaf, create);
    if (!create && fVersions == null) {
      VersionsFileImpl versions = new VersionsFileImpl();
      versions.setCurrentVersion((Versionable) leaf);
      versions.setVersioned(isVersioned(leaf));
      versions.setRevisionNr(getNextRevisionNr(versions));
      return versions;
    }
    return readVersions(leaf, fVersions);
  }

  private Versions readVersions(VFSLeaf leaf, VFSLeaf fVersions) {
    if (fVersions == null) { return new NotVersioned(); }

    VFSContainer fVersionContainer = fVersions.getParentContainer();
    VersionsFileImpl versions = (VersionsFileImpl) XStreamHelper.readObject(mystream, fVersions);
    versions.setVersionFile(fVersions);
    versions.setCurrentVersion((Versionable) leaf);
    if (versions.getRevisionNr() == null || versions.getRevisionNr().length() == 0) {
      versions.setRevisionNr(getNextRevisionNr(versions));
    }

    for (VFSRevision revision : versions.getRevisions()) {
      RevisionFileImpl revisionImpl = (RevisionFileImpl) revision;
      revisionImpl.setContainer(fVersionContainer);
    }
    return versions;
  }

  @Override
  public boolean addVersion(Versionable currentVersion, Identity identity, String comment, InputStream newFile) {
    VFSLeaf currentFile = (VFSLeaf) currentVersion;
    if (addToRevisions(currentVersion, identity, comment)) {
      // copy the content of the new file to the old
      boolean closeInputStream = !(newFile instanceof net.sf.jazzlib.ZipInputStream || newFile instanceof java.util.zip.ZipInputStream);
      if (VFSManager.copyContent(newFile, currentFile, closeInputStream)) { return true; }
    } else {
      log.error("Cannot create a version of this file: " + currentVersion);
    }
    return false;
  }

  @Override
  public boolean move(Versionable currentVersion, VFSContainer container) {
    VFSLeaf currentFile = (VFSLeaf) currentVersion;
    VFSLeaf fVersions = getCanonicalVersionXmlFile(currentFile, true);
    Versions versions = readVersions(currentFile, fVersions);

    VFSContainer versionContainer = getCanonicalVersionFolder(container, true);

    boolean allOk = VFSConstants.YES.equals(versionContainer.copyFrom(fVersions));
    for (VFSRevision revision : versions.getRevisions()) {
      RevisionFileImpl revisionImpl = (RevisionFileImpl) revision;
      VFSLeaf revisionFile = revisionImpl.getFile();
      if (revisionFile != null) {
        allOk &= VFSConstants.YES.equals(versionContainer.copyFrom(revisionFile));
      }
    }

    allOk &= VFSConstants.YES.equals(fVersions.delete());
    for (VFSRevision revision : versions.getRevisions()) {
      VFSLeaf revisionFile = ((RevisionFileImpl) revision).getFile();
      if (revisionFile != null) {
        allOk &= VFSConstants.YES.equals(revisionFile.delete());
      }
    }
    return allOk;
  }

  @Override
  public boolean restore(Versionable currentVersion, VFSRevision version, String comment) {
    VFSLeaf currentFile = (VFSLeaf) currentVersion;
    if(!VFSManager.exists(currentFile)) {
      return false;
    }
   
    // add current version to versions file
    if (addToRevisions(currentVersion, null, comment)) {
      // copy the content of the new file to the old
      if (VFSManager.copyContent(version.getInputStream(), currentFile)) { return true; }
    } else {
      log.error("Cannot create a version of this file: " + currentVersion);
    }

    return false;
  }

  @Override
  public boolean restore(VFSContainer container, VFSRevision revision) {
    String filename = revision.getName();
    VFSItem restoredItem = container.resolve(filename);
    if (restoredItem == null) {
      restoredItem = container.createChildLeaf(filename);
    }
    if (restoredItem instanceof VFSLeaf) {
      VFSLeaf restoredLeaf = (VFSLeaf) restoredItem;
      InputStream inStream = revision.getInputStream();
      if (VFSManager.copyContent(inStream, restoredLeaf)) {
        VFSLeaf versionFile = getCanonicalVersionXmlFile(restoredLeaf, true);
        Versions versions = readVersions(restoredLeaf, versionFile);
        if (versions instanceof VersionsFileImpl) {
          versions.getRevisions().remove(revision);
          ((VersionsFileImpl) versions).setRevisionNr(getNextRevisionNr(versions));
        }
        XStreamHelper.writeObject(mystream, versionFile, versions);
        return true;
      }
    }
    return false;
  }

  @Override
  public boolean deleteRevisions(Versionable currentVersion, List<VFSRevision> versionsToDelete) {
    VFSLeaf currentFile = (VFSLeaf) currentVersion;
    Versions versions = readVersions(currentFile, true);
    List<VFSRevision> allVersions = versions.getRevisions();

    for (VFSRevision versionToDelete : versionsToDelete) {
      RevisionFileImpl versionImpl = (RevisionFileImpl) versionToDelete;
      for (Iterator<VFSRevision> allVersionIt = allVersions.iterator(); allVersionIt.hasNext();) {
        RevisionFileImpl allVersionImpl = (RevisionFileImpl) allVersionIt.next();
        if (allVersionImpl.getFilename() != null && allVersionImpl.getFilename().equals(versionImpl.getFilename())) {
          allVersionIt.remove();
          break;
        }
      }

      VFSLeaf fileToDelete = versionImpl.getFile();
      if (fileToDelete != null) {
        fileToDelete.delete();
      }
    }

    VFSLeaf versionFile = getCanonicalVersionXmlFile(currentFile, true);
    XStreamHelper.writeObject(mystream, versionFile, versions);
    if (currentVersion.getVersions() instanceof VersionsFileImpl) {
      ((VersionsFileImpl) currentVersion.getVersions()).update(versions);
    }
    return true;
  }

  @Override
  public boolean deleteVersions(List<Versions> versions) {
    for(Versions versionToDelete:versions) {
      if(versionToDelete instanceof VersionsFileImpl) {
        VersionsFileImpl versionsImpl = (VersionsFileImpl)versionToDelete;
        VFSLeaf versionFile = versionsImpl.getVersionFile();
        if(versionFile != null) {
          //robust against manual file system manipulation
          versionFile.delete();
        }
        for (VFSRevision revisionToDelete : versionsImpl.getRevisions()) {
          RevisionFileImpl versionImpl = (RevisionFileImpl)revisionToDelete;
          VFSLeaf fileToDelete = versionImpl.getFile();
          if (fileToDelete != null) {
            fileToDelete.delete();
          }
        }
      }
    }
    return true;
  }

  @Override
  public boolean delete(VFSItem item, boolean force) {
    if (item instanceof VFSContainer) {
      if (force) {
        VFSContainer container = (VFSContainer)item;
        VFSContainer versionContainer = getCanonicalVersionFolder(container, false);
        if (versionContainer == null) { return true; }
        return VFSConstants.YES.equals(versionContainer.delete());
      }
      return true;
    } else if (item instanceof VFSLeaf && item instanceof Versionable) {
      VFSLeaf leaf = (VFSLeaf)item;
      if (force) {
        cleanUp(leaf);
      } else {
        addToRevisions((Versionable)leaf, null, null);
      }
    }
    return false;
  }
 
  /**
   * Clean up all revisions files, xml file
   * @param leaf
   */
  private void cleanUp(VFSLeaf leaf) {
    String relPath = getRelPath(leaf);
    if (relPath == null) return; // cannot handle

    File fVersion = new File(getRootVersionsFile(), relPath + ".xml");
    File fParentVersion = fVersion.getParentFile();
    if (!fParentVersion.exists()) return; //already deleted

    VFSLeaf versionLeaf = null;
    if (fVersion.exists()) {
      LocalFolderImpl localVersionContainer = new LocalFolderImpl(fParentVersion);
      versionLeaf = (VFSLeaf) localVersionContainer.resolve(fVersion.getName());
    }
   
    if (versionLeaf == null) return; //already deleted
    Versions versions = readVersions(leaf, versionLeaf);
    for (VFSRevision versionToDelete : versions.getRevisions()) {
      RevisionFileImpl versionImpl = (RevisionFileImpl) versionToDelete;
      VFSLeaf fileToDelete = versionImpl.getFile();
      if (fileToDelete != null) {
        fileToDelete.delete();
      }
    }
    versionLeaf.delete();
  }

  @Override
  public boolean rename(VFSItem item, String newname) {
    if (item instanceof VFSLeaf) {
      VFSLeaf currentFile = (VFSLeaf) item;
      VFSLeaf versionFile = getCanonicalVersionXmlFile(currentFile, true);
      // infinite loop if rename is own versions file
      return VFSConstants.YES.equals(versionFile.rename(newname + ".xml"));
    } else if (item instanceof VFSContainer) {
      VFSContainer container = (VFSContainer) item;
      VFSContainer versionContainer = getCanonicalVersionFolder(container, false);
      if (versionContainer == null) { return true; }
      return VFSConstants.YES.equals(versionContainer.rename(newname));
    }
    return false;
  }

  /**
   * @see org.olat.core.util.vfs.version.VersionsManager#addToRevisions(org.olat.core.util.vfs.version.Versionable, org.olat.core.id.Identity, java.lang.String)
   */
  @Override
  public boolean addToRevisions(Versionable currentVersion, Identity identity, String comment) {
    VFSLeaf currentFile = (VFSLeaf) currentVersion;
   
    VFSLeaf versionFile = getCanonicalVersionXmlFile(currentFile, true);
    if(versionFile == null) {
      return false;//cannot do something with the current file
    }
   
    VFSContainer versionContainer = versionFile.getParentContainer();

    String name = currentFile.getName();

    // read from the
    Versions v = readVersions(currentFile, versionFile);
    if (!(v instanceof VersionsFileImpl)) {
      log.error("Wrong implementation of Versions: " + v);
      return false;
    }
    VersionsFileImpl versions = (VersionsFileImpl) v;

    String uuid = UUID.randomUUID().toString() + "_" + name;
    String versionNr = getNextRevisionNr(versions);
    String currentAuthor = versions.getAuthor();
    long lastModifiedDate = 0;
    if (currentFile instanceof MetaTagged) {
      MetaInfo metaInfo = ((MetaTagged) currentFile).getMetaInfo();
      if(currentAuthor == null) {
        currentAuthor = metaInfo.getAuthor();
      }
      lastModifiedDate = metaInfo.getLastModified();
    }
   
    if(lastModifiedDate <= 0) {
      Calendar cal = Calendar.getInstance();
      cal.setTime(new Date());
      lastModifiedDate = cal.getTimeInMillis();
    }

    RevisionFileImpl newRevision = new RevisionFileImpl();
    newRevision.setName(name);
    newRevision.setFilename(uuid);
    newRevision.setRevisionNr(versionNr);
    newRevision.setComment(versions.getComment());
    newRevision.setAuthor(currentAuthor);
    newRevision.setLastModified(lastModifiedDate);

    if (versions.getRevisions().isEmpty() && currentVersion instanceof MetaTagged) {
      MetaTagged metaTagged = (MetaTagged) currentVersion;
      versions.setCreator(metaTagged.getMetaInfo().getAuthor());
    }

    VFSLeaf target = versionContainer.createChildLeaf(uuid);
    if (VFSManager.copyContent(currentFile, target)) {
      if (identity != null) {
        versions.setAuthor(identity.getName());
      }
      versions.setComment(comment);
      versions.getRevisions().add(newRevision);
      versions.setRevisionNr(getNextRevisionNr(versions));
      XStreamHelper.writeObject(mystream, versionFile, versions);
      if (currentVersion.getVersions() instanceof VersionsFileImpl) {
        ((VersionsFileImpl) currentVersion.getVersions()).update(versions);
      }
      return true;
    } else {
      log.error("Cannot create a version of this file: " + currentVersion);
    }
    return false;
  }

  public String getNextRevisionNr(Versions versions) {
    int maxNumber = 0;
    for (VFSRevision version : versions.getRevisions()) {
      String versionNr = version.getRevisionNr();
      if (versionNr != null && versionNr.length() > 0) {
        try {
          int number = Integer.parseInt(versionNr);
          maxNumber = Math.max(maxNumber, number);
        } catch (Exception ex) {
          // if not a number, don't interest us
        }
      }
    }
    return Integer.toString(maxNumber + 1);
  }

  /**
   * Get the canonical path to the file's meta file.
   *
   * @param bcPath
   * @return String
   */
  private VFSLeaf getCanonicalVersionXmlFile(VFSItem item, boolean create) {
    File f = getOriginFile(item);
    if (!f.exists()) { return null; }

    String relPath = getRelPath(item);
    if (relPath == null) {
      // cannot handle
      return null;
    }

    File fVersion = new File(getRootVersionsFile(), relPath + ".xml");
    File fParentVersion = fVersion.getParentFile();
    if (!fParentVersion.exists() && create) {
      fParentVersion.mkdirs();
    }

    if (fVersion.exists()) {
      LocalFolderImpl localVersionContainer = new LocalFolderImpl(fParentVersion);
      return (VFSLeaf) localVersionContainer.resolve(fVersion.getName());
    } else if (create) {
      LocalFolderImpl localVersionContainer = new LocalFolderImpl(fParentVersion);
      VersionsFileImpl versions = new VersionsFileImpl();
      versions.setVersioned(isVersioned(item));
      versions.setRevisionNr(getNextRevisionNr(versions));
      VFSLeaf fVersions = localVersionContainer.createChildLeaf(fVersion.getName());
      XStreamHelper.writeObject(mystream, fVersions, versions);
      return fVersions;
    }
    return null;
  }

  private VFSContainer getCanonicalVersionFolder(VFSContainer container, boolean create) {
    String relPath = getRelPath(container);
    File fVersion = new File(getRootVersionsFile(), relPath);
    if (fVersion.exists()) { return new LocalFolderImpl(fVersion); }
    if (create) {
      fVersion.mkdirs();
      return new LocalFolderImpl(fVersion);
    }
    return null;
  }

  private String getRelPath(VFSItem item) {
    String relPath = null;
    if (item instanceof NamedContainerImpl) {
      item = ((NamedContainerImpl)item).getDelegate();
    }
    if (item instanceof MergeSource) {
      item = ((MergeSource)item).getRootWriteContainer();
    }
    if (item instanceof OlatRelPathImpl) {
      relPath = ((OlatRelPathImpl) item).getRelPath();
    } else if (item instanceof LocalImpl) {
      LocalImpl impl = (LocalImpl) item;
      String absolutPath = impl.getBasefile().getAbsolutePath();
      if (absolutPath.startsWith(getCanonicalRoot())) {
        relPath = absolutPath.substring(getCanonicalRoot().length());
      }
    }
    return relPath;
  }

  private boolean isVersionFile(VFSItem item) {
    File f = getOriginFile(item);
    if (f == null) return false;

    try {
      String path = f.getCanonicalPath();
      String vPath = getRootVersionsFile().getCanonicalPath();
      return path.startsWith(vPath);
    } catch (IOException e) {
      log.error("Cannot check if this file is a version file: " + item, e);
      return false;
    }
  }

  private boolean isVersioned(VFSItem item) {
    if (item == null) return false;
    VFSContainer parent = item.getParentContainer();
    return FolderConfig.versionsEnabled(parent);
  }

  private File getOriginFile(VFSItem item) {
    if (item instanceof LocalImpl) {
      LocalImpl localImpl = (LocalImpl) item;
      return localImpl.getBasefile();
    }
    if (item instanceof OlatRelPathImpl) {
      OlatRelPathImpl relPath = (OlatRelPathImpl) item;
      return new File(getCanonicalRoot(), relPath.getRelPath());
    }
    return null;
  }

  public String getCanonicalRoot() {
    return FolderConfig.getCanonicalRoot();
  }
 
  public File getRootVersionsFile() {
    if (rootVersionsContainer == null) {
      rootVersionFolder = new File(FolderConfig.getCanonicalVersionRoot());
      if(!rootVersionFolder.exists()) {
        rootVersionFolder.mkdirs();
      }
      rootVersionsContainer = new LocalFolderImpl(rootVersionFolder);
    }
    return rootVersionFolder;
  }

  public VFSContainer getRootVersionsContainer() {
    if (rootVersionsContainer == null) {
      rootVersionFolder = new File(FolderConfig.getCanonicalVersionRoot());
      if(!rootVersionFolder.exists()) {
        rootVersionFolder.mkdirs();
      }
      rootVersionsContainer = new LocalFolderImpl(rootVersionFolder);
    }
    return rootVersionsContainer;
  }
}
TOP

Related Classes of org.olat.core.util.vfs.version.VersionsFileManager

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.