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

org.sirix.page.RevisionRootPage Maven / Gradle / Ivy

/*
 * Copyright (c) 2011, University of Konstanz, Distributed Systems Group All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met: * Redistributions of source code must retain the
 * above copyright notice, this list of conditions and the following disclaimer. * Redistributions
 * in binary form must reproduce the above copyright notice, this list of conditions and the
 * following disclaimer in the documentation and/or other materials provided with the distribution.
 * * Neither the name of the University of Konstanz nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL  BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.sirix.page;

import com.google.common.base.MoreObjects;
import org.sirix.access.DatabaseType;
import org.sirix.access.User;
import org.sirix.access.trx.node.CommitCredentials;
import org.sirix.api.PageReadOnlyTrx;
import org.sirix.api.PageTrx;
import org.sirix.cache.TransactionIntentLog;
import org.sirix.index.IndexType;
import org.sirix.page.delegates.BitmapReferencesPage;
import org.sirix.page.interfaces.Page;
import org.sirix.settings.Constants;

import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.time.Instant;
import java.util.Optional;
import java.util.UUID;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Revision root page holds a reference to the name page as well as the static node page tree.
 */
public final class RevisionRootPage extends AbstractForwardingPage {

  /**
   * Offset of indirect page reference.
   */
  private static final int INDIRECT_DOCUMENT_INDEX_REFERENCE_OFFSET = 0;

  /**
   * Offset of indirect page reference.
   */
  private static final int INDIRECT_CHANGED_NODES_INDEX_REFERENCE_OFFSET = 1;

  /**
   * Offset of node to revisions page reference.
   */
  private static final int INDIRECT_RECORD_TO_REVISIONS_INDEX_REFERENCE_OFFSET = 2;

  /**
   * Offset of path summary page reference.
   */
  private static final int PATH_SUMMARY_REFERENCE_OFFSET = 3;

  /**
   * Offset of name page reference.
   */
  private static final int NAME_REFERENCE_OFFSET = 4;

  /**
   * Offset of CAS page reference.
   */
  private static final int CAS_REFERENCE_OFFSET = 5;

  /**
   * Offset of path page reference.
   */
  private static final int PATH_REFERENCE_OFFSET = 6;

  /**
   * Offset of dewey-ID page reference.
   */
  private static final int DEWEYID_REFERENCE_OFFSET = 7;

  /**
   * Last allocated node key.
   */
  private long maxNodeKeyInDocumentIndex;

  /**
   * Last allocated node key.
   */
  private long maxNodeKeyInChangedNodesIndex;

  /**
   * Last allocated node key.
   */
  private long maxNodeKeyInRecordToRevisionsIndex;

  /**
   * Timestamp of revision.
   */
  private long revisionTimestamp;

  /**
   * The references page instance.
   */
  private final Page delegate;

  /**
   * Revision number.
   */
  private final int revision;

  /**
   * Optional commit message.
   */
  private String commitMessage;

  /**
   * Current maximum level of indirect pages in the document index tree.
   */
  private int currentMaxLevelOfDocumentIndexIndirectPages;

  /**
   * Current maximum level of indirect pages in the changed nodes index tree.
   */
  private int currentMaxLevelOfChangedNodesIndirectPages;

  /**
   * Current maximum level of indirect pages in the record to revisions index tree.
   */
  private int currentMaxLevelOfRecordToRevisionsIndirectPages;

  /**
   * The user, which committed or is probably committing the revision.
   */
  private User user;

  private Instant commitTimestamp;

  /**
   * Create revision root page.
   */
  public RevisionRootPage() {
    delegate = new BitmapReferencesPage(8);
    getOrCreateReference(PATH_SUMMARY_REFERENCE_OFFSET).setPage(new PathSummaryPage());
    getOrCreateReference(NAME_REFERENCE_OFFSET).setPage(new NamePage());
    getOrCreateReference(CAS_REFERENCE_OFFSET).setPage(new CASPage());
    getOrCreateReference(PATH_REFERENCE_OFFSET).setPage(new PathPage());
    getOrCreateReference(DEWEYID_REFERENCE_OFFSET).setPage(new DeweyIDPage());
    revision = Constants.UBP_ROOT_REVISION_NUMBER;
    maxNodeKeyInDocumentIndex = -1L;
    maxNodeKeyInChangedNodesIndex = -1L;
    maxNodeKeyInRecordToRevisionsIndex = -1L;
    currentMaxLevelOfDocumentIndexIndirectPages = 1;
    currentMaxLevelOfChangedNodesIndirectPages = 1;
    currentMaxLevelOfRecordToRevisionsIndirectPages = 1;
  }

  /**
   * Read revision root page.
   *
   * @param in input stream
   */
  protected RevisionRootPage(final DataInput in, final SerializationType type) throws IOException {
    delegate = new BitmapReferencesPage(8, in, type);
    revision = in.readInt();
    maxNodeKeyInDocumentIndex = in.readLong();
    maxNodeKeyInChangedNodesIndex = in.readLong();
    maxNodeKeyInRecordToRevisionsIndex = in.readLong();
    revisionTimestamp = in.readLong();
    if (in.readBoolean()) {
      final byte[] commitMessage = new byte[in.readInt()];
      in.readFully(commitMessage);
      this.commitMessage = new String(commitMessage, Constants.DEFAULT_ENCODING);
    }
    currentMaxLevelOfDocumentIndexIndirectPages = in.readByte() & 0xFF;
    currentMaxLevelOfChangedNodesIndirectPages = in.readByte() & 0xFF;
    currentMaxLevelOfRecordToRevisionsIndirectPages = in.readByte() & 0xFF;

    if (in.readBoolean()) {
      user = new User(in.readUTF(), UUID.fromString(in.readUTF()));
    } else {
      user = null;
    }
  }

  /**
   * Clone revision root page.
   *
   * @param committedRevisionRootPage page to clone
   * @param representRev              revision number to use
   */
  public RevisionRootPage(final RevisionRootPage committedRevisionRootPage, final @NonNegative int representRev) {
    final Page pageDelegate = committedRevisionRootPage.delegate();
    delegate = new BitmapReferencesPage(pageDelegate, ((BitmapReferencesPage) pageDelegate).getBitmap());
    revision = representRev;
    user = committedRevisionRootPage.user;
    maxNodeKeyInDocumentIndex = committedRevisionRootPage.maxNodeKeyInDocumentIndex;
    maxNodeKeyInChangedNodesIndex = committedRevisionRootPage.maxNodeKeyInChangedNodesIndex;
    maxNodeKeyInRecordToRevisionsIndex = committedRevisionRootPage.maxNodeKeyInRecordToRevisionsIndex;
    revisionTimestamp = committedRevisionRootPage.revisionTimestamp;
    commitMessage = committedRevisionRootPage.commitMessage;
    currentMaxLevelOfDocumentIndexIndirectPages = committedRevisionRootPage.currentMaxLevelOfDocumentIndexIndirectPages;
    currentMaxLevelOfChangedNodesIndirectPages = committedRevisionRootPage.currentMaxLevelOfChangedNodesIndirectPages;
    currentMaxLevelOfRecordToRevisionsIndirectPages =
        committedRevisionRootPage.currentMaxLevelOfRecordToRevisionsIndirectPages;
  }

  /**
   * Get path summary page reference.
   *
   * @return path summary page reference
   */
  public PageReference getPathSummaryPageReference() {
    return getOrCreateReference(PATH_SUMMARY_REFERENCE_OFFSET);
  }

  /**
   * Get CAS page reference.
   *
   * @return CAS page reference
   */
  public PageReference getCASPageReference() {
    return getOrCreateReference(CAS_REFERENCE_OFFSET);
  }

  /**
   * Get name page reference.
   *
   * @return name page reference
   */
  public PageReference getNamePageReference() {
    return getOrCreateReference(NAME_REFERENCE_OFFSET);
  }

  /**
   * Get path page reference.
   *
   * @return path page reference
   */
  public PageReference getPathPageReference() {
    return getOrCreateReference(PATH_REFERENCE_OFFSET);
  }

  /**
   * Get indirect page reference of document index.
   *
   * @return Indirect page reference of document index.
   */
  public PageReference getIndirectDocumentIndexPageReference() {
    return getOrCreateReference(INDIRECT_DOCUMENT_INDEX_REFERENCE_OFFSET);
  }

  /**
   * Get indirect page reference of changed nodes index.
   *
   * @return Indirect page reference of changed nodes index.
   */
  public PageReference getIndirectChangedNodesIndexPageReference() {
    return getOrCreateReference(INDIRECT_CHANGED_NODES_INDEX_REFERENCE_OFFSET);
  }

  /**
   * Get indirect page reference.
   *
   * @return Indirect changed nodes index page reference.
   */
  public PageReference getIndirectRecordToRevisionsIndexPageReference() {
    return getOrCreateReference(INDIRECT_RECORD_TO_REVISIONS_INDEX_REFERENCE_OFFSET);
  }

  /**
   * Get dewey ID page reference.
   *
   * @return dewey ID page reference.
   */
  public PageReference getDeweyIdPageReference() {
    return getOrCreateReference(DEWEYID_REFERENCE_OFFSET);
  }

  /**
   * Get timestamp of revision.
   *
   * @return Revision timestamp.
   */
  public long getRevisionTimestamp() {
    return revisionTimestamp;
  }

  /**
   * Get last allocated node key in document index.
   *
   * @return Last allocated node key in document index
   */
  public long getMaxNodeKeyInDocumentIndex() {
    return maxNodeKeyInDocumentIndex;
  }

  /**
   * Increment number of nodes by one while allocating another key in document index.
   */
  public long incrementAndGetMaxNodeKeyInDocumentIndex() {
    return ++maxNodeKeyInDocumentIndex;
  }

  /**
   * Set the maximum node key in the revision in the document index.
   *
   * @param maxNodeKeyInDocumentIndex new maximum node key
   */
  public void setMaxNodeKeyInDocumentIndex(final @NonNegative long maxNodeKeyInDocumentIndex) {
    this.maxNodeKeyInDocumentIndex = maxNodeKeyInDocumentIndex;
  }

  /**
   * Get last allocated node key in changed nodes index.
   *
   * @return Last allocated node key in changed nodes index
   */
  public long getMaxNodeKeyInChangedNodesIndex() {
    return maxNodeKeyInChangedNodesIndex;
  }

  /**
   * Increment number of nodes by one while allocating another key in changed nodes index.
   */
  public long incrementAndGetMaxNodeKeyInChangedNodesIndex() {
    return ++maxNodeKeyInChangedNodesIndex;
  }

  /**
   * Set the maximum node key in the revision in the changed nodes index.
   *
   * @param maxNodeKeyInChangedNodesIndex new maximum node key
   */
  public void setMaxNodeKeyInInChangedNodesIndex(final @NonNegative long maxNodeKeyInChangedNodesIndex) {
    this.maxNodeKeyInChangedNodesIndex = maxNodeKeyInChangedNodesIndex;
  }

  /**
   * Get last allocated node key in record to revisions index.
   *
   * @return Last allocated node key in record to revisions index
   */
  public long getMaxNodeKeyInRecordToRevisionsIndex() {
    return maxNodeKeyInRecordToRevisionsIndex;
  }

  /**
   * Increment number of nodes by one while allocating another key in record to revisions index.
   */
  public long incrementAndGetMaxNodeKeyInRecordToRevisionsIndex() {
    return ++maxNodeKeyInRecordToRevisionsIndex;
  }

  /**
   * Set the maximum node key in the revision in the record to revisions index.
   *
   * @param maxNodeKeyInRecordToRevisionsIndex new maximum node key
   */
  public void setMaxNodeKeyInRecordToRevisionsIndex(final @NonNegative long maxNodeKeyInRecordToRevisionsIndex) {
    this.maxNodeKeyInRecordToRevisionsIndex = maxNodeKeyInRecordToRevisionsIndex;
  }

  /**
   * Only commit whole subtree if it's the currently added revision.
   * 

* {@inheritDoc} */ @Override public void commit(@NonNull final PageTrx pageWriteTrx) { if (revision == pageWriteTrx.getUberPage().getRevisionNumber()) { super.commit(pageWriteTrx); } } public void setCommitTimestamp(final Instant revisionTimestamp) { checkNotNull(revisionTimestamp); final long revisionTimestampToSet = revisionTimestamp.toEpochMilli(); if (this.revisionTimestamp > revisionTimestampToSet || revisionTimestamp.isAfter(Instant.now())) { throw new IllegalStateException("Revision timestamp must be bigger than previous revision timestamp, but not bigger than current time."); } this.commitTimestamp = revisionTimestamp; } @Override public void serialize(final DataOutput out, final SerializationType type) throws IOException { revisionTimestamp = commitTimestamp == null ? Instant.now().toEpochMilli() : commitTimestamp.toEpochMilli(); delegate.serialize(checkNotNull(out), checkNotNull(type)); out.writeInt(revision); out.writeLong(maxNodeKeyInDocumentIndex); out.writeLong(maxNodeKeyInChangedNodesIndex); out.writeLong(maxNodeKeyInRecordToRevisionsIndex); out.writeLong(revisionTimestamp); out.writeBoolean(commitMessage != null); if (commitMessage != null) { final byte[] commitMessage = this.commitMessage.getBytes(Constants.DEFAULT_ENCODING); out.writeInt(commitMessage.length); out.write(commitMessage); } out.writeByte(currentMaxLevelOfDocumentIndexIndirectPages); out.writeByte(currentMaxLevelOfChangedNodesIndirectPages); out.writeByte(currentMaxLevelOfRecordToRevisionsIndirectPages); final boolean hasUser = user != null; out.writeBoolean(hasUser); if (hasUser) { out.writeUTF(user.getName()); out.writeUTF(user.getId().toString()); } } public int getCurrentMaxLevelOfDocumentIndexIndirectPages() { return currentMaxLevelOfDocumentIndexIndirectPages; } public int incrementAndGetCurrentMaxLevelOfDocumentIndexIndirectPages() { return ++currentMaxLevelOfDocumentIndexIndirectPages; } public int getCurrentMaxLevelOfChangedNodesIndexIndirectPages() { return currentMaxLevelOfChangedNodesIndirectPages; } public int incrementAndGetCurrentMaxLevelOfChangedNodesIndexIndirectPages() { return ++currentMaxLevelOfChangedNodesIndirectPages; } public int getCurrentMaxLevelOfRecordToRevisionsIndexIndirectPages() { return currentMaxLevelOfRecordToRevisionsIndirectPages; } public int incrementAndGetCurrentMaxLevelOfRecordToRevisionsIndexIndirectPages() { return ++currentMaxLevelOfRecordToRevisionsIndirectPages; } @Override public String toString() { return MoreObjects.toStringHelper(this) .add("revisionTimestamp", revisionTimestamp) .add("maxNodeKey", maxNodeKeyInDocumentIndex) .add("delegate", delegate) .add("nodePage", getOrCreateReference(INDIRECT_DOCUMENT_INDEX_REFERENCE_OFFSET)) .add("namePage", getOrCreateReference(NAME_REFERENCE_OFFSET)) .add("pathSummaryPage", getOrCreateReference(PATH_SUMMARY_REFERENCE_OFFSET)) .add("pathPage", getOrCreateReference(PATH_REFERENCE_OFFSET)) .add("CASPage", getOrCreateReference(CAS_REFERENCE_OFFSET)) .add("deweyIDPage", getOrCreateReference(DEWEYID_REFERENCE_OFFSET)) .toString(); } @Override protected Page delegate() { return delegate; } /** * Initialize document index tree. * * @param databaseType The type of database. * @param pageReadTrx {@link PageReadOnlyTrx} instance * @param log the transaction intent log */ public void createDocumentIndexTree(final DatabaseType databaseType, final PageReadOnlyTrx pageReadTrx, final TransactionIntentLog log) { PageReference reference = getIndirectDocumentIndexPageReference(); if (reference.getPage() == null && reference.getKey() == Constants.NULL_ID_LONG && reference.getLogKey() == Constants.NULL_ID_INT && reference.getPersistentLogKey() == Constants.NULL_ID_LONG) { PageUtils.createTree(databaseType, reference, IndexType.DOCUMENT, pageReadTrx, log); incrementAndGetMaxNodeKeyInDocumentIndex(); } } /** * Initialize record to revisions index tree. * * @param databaseType The type of database. * @param pageReadTrx {@link PageReadOnlyTrx} instance * @param log the transaction intent log */ public void createChangedNodesIndexTree(final DatabaseType databaseType, final PageReadOnlyTrx pageReadTrx, final TransactionIntentLog log) { PageReference reference = getIndirectChangedNodesIndexPageReference(); if (reference.getPage() == null && reference.getKey() == Constants.NULL_ID_LONG && reference.getLogKey() == Constants.NULL_ID_INT && reference.getPersistentLogKey() == Constants.NULL_ID_LONG) { PageUtils.createTree(databaseType, reference, IndexType.CHANGED_NODES, pageReadTrx, log); incrementAndGetMaxNodeKeyInChangedNodesIndex(); } } /** * Initialize changed records index tree. * * @param databaseType The type of database. * @param pageReadTrx {@link PageReadOnlyTrx} instance * @param log the transaction intent log */ public void createRecordToRevisionsIndexTree(final DatabaseType databaseType, final PageReadOnlyTrx pageReadTrx, final TransactionIntentLog log) { PageReference reference = getIndirectRecordToRevisionsIndexPageReference(); if (reference.getPage() == null && reference.getKey() == Constants.NULL_ID_LONG && reference.getLogKey() == Constants.NULL_ID_INT && reference.getPersistentLogKey() == Constants.NULL_ID_LONG) { PageUtils.createTree(databaseType, reference, IndexType.RECORD_TO_REVISIONS, pageReadTrx, log); incrementAndGetMaxNodeKeyInRecordToRevisionsIndex(); } } /** * Get the revision number. * * @return revision number */ public int getRevision() { return revision; } public void setCommitMessage(final String commitMessage) { this.commitMessage = commitMessage; } public void setUser(final User user) { this.user = user; } public CommitCredentials getCommitCredentials() { return new CommitCredentials(user, commitMessage); } public Optional getUser() { return Optional.ofNullable(user); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy