Package org.jboss.errai.otec.client

Source Code of org.jboss.errai.otec.client.TransactionLogImpl$StateSnapshot

/*
* Copyright 2013 JBoss, by Red Hat, Inc
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jboss.errai.otec.client;

import java.util.*;

import org.jboss.errai.otec.client.mutation.Mutation;
import org.jboss.errai.otec.client.operation.OTOperation;

/**
* @author Christian Sadilek <csadilek@redhat.com>
* @author Mike Brock
*/
public class TransactionLogImpl implements TransactionLog {
  private final Object lock = new Object();

  private final List<StateSnapshot> stateSnapshots = new LinkedList<StateSnapshot>();
  private final List<OTOperation> transactionLog = new LinkedList<OTOperation>();
  private final OTEntity entity;

  private TransactionLogImpl(final OTEntity entity) {
    this.entity = entity;
    stateSnapshots.add(new StateSnapshot(entity.getRevision(), entity.getState().snapshot()));
  }

  public static TransactionLog createTransactionLog(final OTEntity entity) {
    return new TransactionLogImpl(entity);
  }

  @Override
  public Object getLock() {
    return lock;
  }

  @Override
  public List<OTOperation> getLog() {
    synchronized (lock) {
      return transactionLog;
    }
  }

  @Override
  public int purgeTo(final int revision) {
    if (revision < 0) {
      return 0;
    }
    synchronized (lock) {
      cleanLogTo(revision);

      final LogQuery effectiveStateForRevision = getEffectiveStateForRevision(revision);

      makeSnapshot(revision, effectiveStateForRevision.getEffectiveState());

      int purged = 0;
      final Iterator<OTOperation> iterator = transactionLog.iterator();
      while (iterator.hasNext()) {
        if (iterator.next().getRevision() < revision) {
          purged++;
          iterator.remove();
        }
        else {
          break;
        }
      }

      if (stateSnapshots.size() > 1) {
        final Iterator<StateSnapshot> iterator1 = stateSnapshots.iterator();
        while (iterator1.hasNext()) {
          if (iterator1.next().getRevision() < revision) {
            iterator1.remove();
          }
          else {
            break;
          }
        }
      }

      return purged;
    }
  }

  @Override
  public void pruneFromOperation(final OTOperation operation) {
    synchronized (lock) {
      final int index = transactionLog.indexOf(operation);
      if (index == -1) {
        return;
      }

      final ListIterator<OTOperation> delIter = transactionLog.listIterator(index);
      while (delIter.hasNext()) {
        entity.decrementRevisionCounter();
        final OTOperation next = delIter.next();

        final OTOperation outerPath = next.getOuterPath();
        if (outerPath != next && outerPath.getTransformedFrom() != null) {
          if (outerPath.getTransformedFrom().getRemoteOp().equals(next)) {
            outerPath.removeFromCanonHistory();
            continue;
          }
        }

        next.removeFromCanonHistory();
      }
    }
  }

  @Override
  public List<OTOperation> getLocalOpsSinceRemoteOperation(final OTOperation operation, final boolean includeNonCanon) {
    synchronized (lock) {
      if (transactionLog.isEmpty()) {
        return Collections.emptyList();
      }

      final ListIterator<OTOperation> operationListIterator = transactionLog.listIterator(transactionLog.size());
      final List<OTOperation> operationList = new ArrayList<OTOperation>();
      final int revision = operation.getRevision();

      while (operationListIterator.hasPrevious()) {
        final OTOperation previous = operationListIterator.previous();

        if (!includeNonCanon && !previous.isCanon()) {
          continue;
        }

        operationList.add(previous);

        if (previous.getRevision() == revision || previous.getRevisionHash().equals(operation.getRevisionHash())) {
          Collections.reverse(operationList);
          return operationList;
        }
      }

      if ((revision - 1) == transactionLog.get(transactionLog.size() - 1).getRevision()) {
        return Collections.emptyList();
      }
      else {
        throw new OTException("unable to find revision in log: " + revision + "; op=" + operation);
      }
    }
  }

  @Override
  public List<OTOperation> getRemoteOpsSinceRevision(final String agentId, final int rev) {
    synchronized (lock) {
      final ListIterator<OTOperation> iter = transactionLog.listIterator(transactionLog.size());

      final List<OTOperation> collect = new LinkedList<OTOperation>();
      while (iter.hasPrevious()) {
        final OTOperation previous = iter.previous();
        if (previous.getRevision() < rev) {
          break;
        }
        if (agentId.equals(previous.getAgentId())) {
          collect.add(previous);
        }
      }

      Collections.reverse(collect);

      return collect;
    }
  }

  @Override
  public List<OTOperation> getCanonLog() {
    synchronized (lock) {
      final List<OTOperation> canonLog = new ArrayList<OTOperation>(transactionLog.size());
      for (final OTOperation operation : transactionLog) {
        if (operation.isCanon()) {
          canonLog.add(operation);
        }
      }
      return canonLog;
    }
  }

  @SuppressWarnings("unchecked")
  @Override
  public LogQuery getEffectiveStateForRevision(final int revision) {
    synchronized (lock) {
      final StateSnapshot latestSnapshotState = getLatestParentSnapshot(revision);
      final State stateToTranslate = latestSnapshotState.getState().snapshot();

      final ListIterator<OTOperation> operationListIterator
          = transactionLog.listIterator(transactionLog.size());

      while (operationListIterator.hasPrevious()) {
        if (operationListIterator.previous().getRevision() == latestSnapshotState.getRevision()) {
          break;
        }
      }

      final Set<OTOperation> contingent = new LinkedHashSet<OTOperation>();
      final List<OTOperation> needsMerge = new LinkedList<OTOperation>();
      while (operationListIterator.hasNext()) {
        final OTOperation op = operationListIterator.next();

        if (!op.isCanon()) {
          continue;
        }

        if (op.getRevision() < revision) {
          for (final Mutation mutation : op.getMutations()) {
            mutation.apply(stateToTranslate);
          }
          contingent.add(op.getOuterPath());
        }
        else {
          needsMerge.add(op);
        }
      }

      return new LogQuery(stateToTranslate, contingent, needsMerge);
    }
  }

  private StateSnapshot getLatestParentSnapshot(final int revision) {
    synchronized (lock) {
      final ListIterator<StateSnapshot> snapshotListIterator = stateSnapshots.listIterator(stateSnapshots.size());

      while (snapshotListIterator.hasPrevious()) {
        final StateSnapshot stateSnapshot = snapshotListIterator.previous();
        if (stateSnapshot.getRevision() <= revision) {
          return stateSnapshot;
        }
      }

      throw new RuntimeException("no parent state for: " + revision);
    }
  }

  private void makeSnapshot(final int revision, final State state) {
    stateSnapshots.add(new StateSnapshot(revision, state.getTransientState()));
    cleanLog();
  }

  @Override
  public void appendLog(final OTOperation operation) {
    synchronized (lock) {
      if (operation.isNoop()) {
        return;
      }

      transactionLog.add(operation);
    }
  }

  @Override
  public void insertLog(final int revision, final OTOperation operation) {
    synchronized (lock) {
      final ListIterator<OTOperation> operationListIterator
          = transactionLog.listIterator(transactionLog.size());

      while (operationListIterator.hasPrevious()) {
        if (operationListIterator.previous().getRevision() == revision) {
          operationListIterator.set(operation);
          break;
        }
      }
    }
  }

  @Override
  public void markDirty() {
    synchronized (lock) {
    }
  }

  @Override
  public void snapshot() {
    makeSnapshot(entity.getRevision(), entity.getState());
  }

  @Override
  public void cleanLog() {
    cleanLogTo(entity.getRevision());
  }

  private void cleanLogTo(final int rev) {
    synchronized (lock) {
      final Set<OTOperation> applied = new HashSet<OTOperation>();

      final Iterator<OTOperation> iterator = transactionLog.iterator();
      while (iterator.hasNext()) {
        final OTOperation next = iterator.next();
        if (next.getRevision() > rev) {
          return;
        }

        if (!next.isCanon() || applied.contains(next)) {
          iterator.remove();
        }
        else {
          applied.add(next.getOuterPath());
        }
      }
    }
  }

  @Override
  public String toString() {
    return Arrays.toString(getCanonLog().toArray());
  }

  private static class StateSnapshot {
    private final int revision;
    private final State state;

    private StateSnapshot(final int revision, final State state) {
      this.revision = revision;
      this.state = state;
    }

    private int getRevision() {
      return revision;
    }

    private State getState() {
      return state;
    }
  }
}
TOP

Related Classes of org.jboss.errai.otec.client.TransactionLogImpl$StateSnapshot

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.