Package mil.nga.giat.geowave.vector.plugin.transaction

Source Code of mil.nga.giat.geowave.vector.plugin.transaction.GeoWaveTransactionManagement

package mil.nga.giat.geowave.vector.plugin.transaction;

import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import mil.nga.giat.geowave.accumulo.util.VisibilityTransformer;
import mil.nga.giat.geowave.index.ByteArrayId;
import mil.nga.giat.geowave.store.CloseableIterator;
import mil.nga.giat.geowave.vector.plugin.GeoWaveDataStoreComponents;
import mil.nga.giat.geowave.vector.plugin.lock.LockingManagement;

import org.apache.commons.lang3.tuple.Pair;
import org.geotools.data.Transaction;
import org.geotools.factory.Hints;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Multimap;

/**
* Captures changes made to a FeatureStore prior to being committed.
* <p>
* This is used to simulate the functionality of a database including
* transaction independence.
*
*
* @source $URL$
*/

public class GeoWaveTransactionManagement implements
    GeoWaveTransaction
{
  /** Map of modified features; by feature id */
  private final Map<String, ModifiedFeature> modifiedFeatures = new ConcurrentHashMap<String, ModifiedFeature>();
  private final Multimap<String, SimpleFeature> removedFeatures = LinkedListMultimap.create();

  /** List of added feature ids; values stored in added above */
  private final Map<String, List<ByteArrayId>> addedFidList = new HashMap<String, List<ByteArrayId>>();

  private final GeoWaveDataStoreComponents components;

  private final LockingManagement lockingManager;

  private final Transaction transaction;

  private final String txID;

  private final String typeName;

  private class ModifiedFeature
  {
    public ModifiedFeature(
        final SimpleFeature oldFeature,
        final SimpleFeature newFeature,
        boolean alreadyWritten ) {
      super();
      this.newFeature = newFeature;
      this.oldFeature = oldFeature;
      this.alreadyWritten = alreadyWritten;
    }

    final boolean alreadyWritten;
    final SimpleFeature newFeature;
    final SimpleFeature oldFeature;

  }

  /** Simple object used for locking */
  Object mutex;

  /**
   * Create an empty Diff
   *
   * @throws IOException
   */
  public GeoWaveTransactionManagement(
      final GeoWaveDataStoreComponents components,
      final String typeName,
      final Transaction transaction,
      LockingManagement lockingManager,
      String txID )
      throws IOException {
    this.components = components;
    mutex = this;
    this.typeName = typeName;
    this.transaction = transaction;
    this.lockingManager = lockingManager;
    this.txID = txID;
  }

  /**
   * Check if modifiedFeatures and addedFeatures are empty.
   *
   * @return true if Diff is empty
   */
  public boolean isEmpty() {
    synchronized (mutex) {
      return modifiedFeatures.isEmpty() && addedFidList.isEmpty() && this.removedFeatures.isEmpty();
    }
  }

  /**
   * Clear diff - called during rollback.
   */
  public void clear() {
    synchronized (mutex) {
      addedFidList.clear();
      modifiedFeatures.clear();
      removedFeatures.clear();
    }
  }

  /**
   * Record a modification to the indicated fid
   *
   * @param fid
   * @param f
   *            replacement feature; null to indicate remove
   */
  public void modify(
      String fid,
      SimpleFeature original,
      SimpleFeature updated )
      throws IOException {

    lockingManager.lock(
        transaction,
        fid);
    // assumptions: (1) will not get a modification to a deleted feature
    // thus, only contents of the removed features collection for this
    // feature relate to moving bounds.
    // @see {@link #interweaveTransaction(CloseableIterator<SimpleFeature>)}
    //
    // Cannot assume that a modification occurs for a newly added fid

    // TODO: skipping this for now. creates a problem because
    // the row IDs may or maynot change. If they change completely, then
    // it is not an issue. However, a mix of changed or unchanged means
    // that the original rows become invisible for the duration of the
    // transaction

    // The problem now is that the bounded query may not return the moved
    // record, if it has moved outside
    // the query space. oh well!

    ModifiedFeature modRecord = modifiedFeatures.get(fid);

    if (!updated.getBounds().equals(
        original.getBounds())) {

      // retain original--original position is removed later.
      // The original feature needs to be excluded in a query
      // and removed at commit
      this.removedFeatures.put(
          fid,
          original);

    }
    if ((modRecord != null && modRecord.alreadyWritten) || addedFidList.containsKey(fid)) {
      this.components.writeCommit(
          updated,
          this);
      synchronized (mutex) {
        modifiedFeatures.put(
            fid,
            new ModifiedFeature(
                modRecord.oldFeature,
                updated,
                true));
      }
    }
    else {
      synchronized (mutex) {
        modifiedFeatures.put(
            fid,
            new ModifiedFeature(
                modRecord == null ? original : modRecord.oldFeature,
                updated,
                false));
      }
    }
    ReferencedEnvelope bounds = new ReferencedEnvelope(
        (CoordinateReferenceSystem) null);
    bounds.include(original.getBounds());
    bounds.include(updated.getBounds());
    components.getGTstore().getListenerManager().fireFeaturesChanged(
        components.getAdapter().getType().getTypeName(),
        transaction,
        bounds,
        false);

  }

  public void add(
      String fid,
      SimpleFeature feature )
      throws IOException {
    feature.getUserData().put(
        Hints.USE_PROVIDED_FID,
        true);
    if (feature.getUserData().containsKey(
        Hints.PROVIDED_FID)) {
      String providedFid = (String) feature.getUserData().get(
          Hints.PROVIDED_FID);
      feature.getUserData().put(
          Hints.PROVIDED_FID,
          providedFid);
    }
    else {
      feature.getUserData().put(
          Hints.PROVIDED_FID,
          feature.getID());
    }
    List<ByteArrayId> rowIDS = this.components.write(
        feature,
        this);
    synchronized (mutex) {
      addedFidList.put(
          fid,
          rowIDS); // preserve order features are added
                // in
    }
    components.getGTstore().getListenerManager().fireFeaturesAdded(
        components.getAdapter().getType().getTypeName(),
        transaction,
        ReferencedEnvelope.reference(feature.getBounds()),
        false);
  }

  public void remove(
      String fid,
      SimpleFeature feature )
      throws IOException {
    synchronized (mutex) {
      if (addedFidList.remove(fid) != null)
        this.components.remove(
            feature,
            this);
      else {
        // will remove at the end of the transaction, except ones
        // created in the transaction.
        removedFeatures.put(
            fid,
            feature);
        modifiedFeatures.remove(fid);
      }
    }
    components.getGTstore().getListenerManager().fireFeaturesRemoved(
        components.getAdapter().getType().getTypeName(),
        transaction,
        ReferencedEnvelope.reference(feature.getBounds()),
        false);
  }

  public void rollback()
      throws IOException {
    for (String fid : this.addedFidList.keySet()) {
      components.remove(
          fid,
          this);
    }
    this.clear();
  }

  @Override
  public String[] composeAuthorizations() {
    final String[] initialAuths = this.components.getGTstore().getAuthorizationSPI().getAuthorizations();
    final String[] newAuths = new String[initialAuths.length + 1];
    System.arraycopy(
        initialAuths,
        0,
        newAuths,
        0,
        initialAuths.length);
    newAuths[initialAuths.length] = this.txID;
    return newAuths;
  }

  @Override
  public String composeVisibility() {
    return this.txID;
  }

  public String getID() {
    return txID;
  }

  public void commit()
      throws IOException {
    final Iterator<Pair<SimpleFeature, SimpleFeature>> updateIt = getUpdates();

    final String transId = "\\(?" + txID + "\\)?";
    VisibilityTransformer visibilityTransformer = new VisibilityTransformer(
        "&?" + transId,
        "");
    for (Collection<ByteArrayId> rowIDs : this.addedFidList.values()) {
      this.components.replaceDataVisibility(
          this,
          rowIDs,
          visibilityTransformer);
    }

    this.components.replaceStatsVisibility(
        this,
        visibilityTransformer);

    final Iterator<SimpleFeature> removeIt = this.removedFeatures.values().iterator();

    while (removeIt.hasNext()) {
      final SimpleFeature delFeatured = removeIt.next();
      components.remove(
          delFeatured,
          this);
      ModifiedFeature modFeature = this.modifiedFeatures.get(delFeatured.getID());
      // only want notify updates to existing (not new) features
      if (modFeature == null || modFeature.alreadyWritten) components.getGTstore().getListenerManager().fireFeaturesRemoved(
          typeName,
          transaction,
          ReferencedEnvelope.reference(delFeatured.getBounds()),
          true);
    }

    while (updateIt.hasNext()) {
      final Pair<SimpleFeature, SimpleFeature> pair = updateIt.next();
      components.writeCommit(
          pair.getRight(),
          this);
      ReferencedEnvelope bounds = new ReferencedEnvelope(
          (CoordinateReferenceSystem) null);
      bounds.include(pair.getLeft().getBounds());
      bounds.include(pair.getRight().getBounds());
      components.getGTstore().getListenerManager().fireFeaturesChanged(
          typeName,
          transaction,
          ReferencedEnvelope.reference(pair.getRight().getBounds()),
          true);
    }

  }

  private Iterator<Pair<SimpleFeature, SimpleFeature>> getUpdates() {
    final Iterator<Entry<String, ModifiedFeature>> entries = this.modifiedFeatures.entrySet().iterator();
    return new Iterator<Pair<SimpleFeature, SimpleFeature>>() {

      Pair<SimpleFeature, SimpleFeature> pair = null;

      @Override
      public boolean hasNext() {
        while (entries.hasNext() && pair == null) {
          Entry<String, ModifiedFeature> entry = entries.next();
          if (!entry.getValue().alreadyWritten)
            pair = Pair.of(
                entry.getValue().oldFeature,
                entry.getValue().newFeature);
          else
            pair = null;
        }
        return pair != null;
      }

      @Override
      public Pair<SimpleFeature, SimpleFeature> next() {
        final Pair<SimpleFeature, SimpleFeature> retVal = pair;
        pair = null;
        return retVal;
      }

      @Override
      public void remove() {}
    };
  }

  @Override
  public CloseableIterator<SimpleFeature> interweaveTransaction(
      final CloseableIterator<SimpleFeature> it ) {
    return new CloseableIterator<SimpleFeature>() {

      SimpleFeature feature = null;

      @Override
      public boolean hasNext() {
        while (it.hasNext() && feature == null) {
          feature = it.next();
          ModifiedFeature modRecord = GeoWaveTransactionManagement.this.modifiedFeatures.get(feature.getID());
          // exclude removed features
          // and include updated features not written yet.
          Collection<SimpleFeature> oldFeatures = GeoWaveTransactionManagement.this.removedFeatures.get(feature.getID());

          if (modRecord != null)
            feature = modRecord.newFeature;
          else if (oldFeatures != null && !oldFeatures.isEmpty()) // TODO:
                                      // need
                                      // to
                                      // check
                                      // if
                                      // the
                                      // removed
                                      // feature
                                      // was
                                      // just
                                      // moved
            // meaning its original matches the boundaries of this
            // 'feature'. matchesOne(oldFeatures, feature))
            feature = null;

        }
        return feature != null;
      }

      @Override
      public SimpleFeature next() {
        final SimpleFeature retVal = feature;
        feature = null;
        return retVal;
      }

      @Override
      public void remove() {
        GeoWaveTransactionManagement.this.removedFeatures.put(
            feature.getID(),
            feature);
      }

      @Override
      public void close()
          throws IOException {
        it.close();
      }

    };
  }

}
TOP

Related Classes of mil.nga.giat.geowave.vector.plugin.transaction.GeoWaveTransactionManagement

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.