/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2011, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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 * Lesser General Public License for more details. */ package org.geoserver.data.geogit; import static org.geotools.data.Transaction.AUTO_COMMIT; import java.io.IOException; import java.util.List; import org.geogit.api.GeoGIT; import org.geogit.api.ObjectId; import org.geogit.api.Ref; import org.geogit.api.RevTree; import org.geogit.repository.Repository; import org.geogit.repository.WorkingTree; import org.geoserver.data.versioning.SimpleVersioningFeatureStore; import org.geoserver.data.versioning.decorator.VersionFilters; import org.geoserver.data.versioning.decorator.VersionQuery; import org.geotools.data.FeatureReader; import org.geotools.data.Transaction; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.feature.FeatureCollection; import org.geotools.feature.NameImpl; import org.geotools.filter.identity.ResourceIdImpl; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; import org.opengis.feature.type.Name; import org.opengis.filter.Filter; import org.opengis.filter.Id; import org.opengis.filter.identity.FeatureId; import org.opengis.filter.identity.Identifier; import org.opengis.filter.identity.ResourceId; import com.google.common.base.Preconditions; import com.google.common.base.Throwables; import com.google.common.collect.Lists; @SuppressWarnings("rawtypes") public class GeoGitFeatureStore extends GeoGitFeatureSource implements SimpleVersioningFeatureStore { private Transaction transaction; public GeoGitFeatureStore(final SimpleFeatureType type, final GeoGitDataStore store) { super(type, store); transaction = Transaction.AUTO_COMMIT; } /** * @return the object id of the current HEAD's commit if we're not inside a * transaction, or {@code null} if we're inside a transaction */ @Override public RevTree getCurrentVersion() { if (getTransaction() == null || Transaction.AUTO_COMMIT.equals(getTransaction())) { return super.getCurrentVersion(); } // assume HEAD is at MASTER try { final Name typeName = this.type.getName(); Repository repository = dataStore.getRepository(); WorkingTree workingTree = repository.getWorkingTree(); RevTree typeTree = workingTree.getStagedVersion(typeName); return typeTree; } catch (Exception e) { throw new RuntimeException(e); } } @Override protected ObjectId getRootTreeId() { final Transaction transaction = getTransaction(); if (null == transaction || Transaction.AUTO_COMMIT.equals(transaction)) { // no transaction in progress, return the repository's root return super.getRootTreeId(); } // transaction in progress, return null to signal querying the index // first return null; // Repository repository = dataStore.getRepository(); // StagingArea index = repository.getIndex(); // RevTree staged = index.getStaged(); // ObjectId stagedRootId = staged.getId(); // return stagedRootId; } /** * @see org.geotools.data.FeatureStore#getTransaction() */ @Override public Transaction getTransaction() { return transaction; } /** * @see org.geotools.data.FeatureStore#setTransaction(org.geotools.data.Transaction) */ @Override public void setTransaction(final Transaction transaction) { Preconditions.checkArgument(transaction != null, "Transaction is null, did you mean Transaction.AUTO_COMMIT?"); this.transaction = transaction; } /** * @see org.geotools.data.FeatureStore#addFeatures(org.geotools.feature.FeatureCollection) */ @Override public List<FeatureId> addFeatures( FeatureCollection<SimpleFeatureType, SimpleFeature> collection) throws IOException { Preconditions.checkNotNull(collection); Preconditions.checkNotNull(collection.getSchema()); Preconditions.checkArgument(type.getName().equals( collection.getSchema().getName())); checkTransaction(); final Name typeName = getSchema().getName(); final VersioningTransactionState versioningState = getVersioningState(); List<FeatureId> insertedResourceIds; try { insertedResourceIds = versioningState.stageInsert(typeName, collection); } catch (Exception e) { throw new IOException(e); } return insertedResourceIds; } /** * @see org.geotools.data.FeatureStore#modifyFeatures(org.opengis.feature.type.Name[], * java.lang.Object[], org.opengis.filter.Filter) */ @Override public void modifyFeatures(final Name[] attributeNames, final Object[] attributeValues, final Filter filter) throws IOException { checkTransaction(); // throws exception if filter has a resourceid that doesn't match the // current version checkEditFilterMatchesCurrentVersion(filter); final SimpleFeatureCollection affectedFeatures = getFeatures(filter); final FeatureCollection newValues = new ModifyingFeatureCollection( affectedFeatures, attributeNames, attributeValues); VersioningTransactionState versioningState = getVersioningState(); try { versioningState.stageUpdate(type.getName(), newValues); } catch (Exception e) { throw new IOException(e); } } /** * @see org.geotools.data.FeatureStore#removeFeatures(org.opengis.filter.Filter) */ @Override public void removeFeatures(final Filter filter) throws IOException { Preconditions.checkNotNull(filter); checkTransaction(); FeatureCollection removed = getFeatures(filter); try { Name typeName = getSchema().getName(); VersioningTransactionState versioningState = getVersioningState(); versioningState.stageDelete(typeName, filter, removed); } catch (Exception e) { Throwables.propagate(e); } } /** * Throws an IllegalArgumentException if {@code filter} contains a resource * filter that doesn't match the current version of a feature * * @param filter original upate filter */ private void checkEditFilterMatchesCurrentVersion(final Filter filter) { final Id versionFilter = VersionFilters.getVersioningFilter(filter); if (versionFilter == null) { return; } final Repository repository = dataStore.getRepository(); // don't allow non current versions GeoGIT ggit = new GeoGIT(repository); VersionQuery query = new VersionQuery(ggit, getSchema().getName()); for (Identifier id : versionFilter.getIdentifiers()) { ResourceId rid = (ResourceId) id; List<Ref> requested; List<Ref> current; try { requested = Lists.newArrayList(query.get(rid)); current = Lists.newArrayList(query.get(new ResourceIdImpl(rid .getID(), null))); } catch (Exception e) { throw new RuntimeException(e); } if (!current.equals(requested)) { throw new IllegalArgumentException( "Requested resource id filter doesn't match curent version for feature " + rid.getID()); } } } /** * @see org.geotools.data.FeatureStore#setFeatures(org.geotools.data.FeatureReader) */ @Override public void setFeatures( FeatureReader<SimpleFeatureType, SimpleFeature> reader) throws IOException { throw new UnsupportedOperationException("do versioning!"); } private void checkTransaction() { if (Transaction.AUTO_COMMIT.equals(getTransaction())) { throw new UnsupportedOperationException( "AUTO_COMMIT is not supported for versioned Feature Types"); } } protected VersioningTransactionState getVersioningState() { Transaction transaction = getTransaction(); if (AUTO_COMMIT.equals(transaction)) { return VersioningTransactionState.VOID; } final Object key = GeoGitDataStore.class; VersioningTransactionState state = (VersioningTransactionState) transaction .getState(key); if (state == null) { Repository repository = dataStore.getRepository(); state = new VersioningTransactionState(new GeoGIT(repository)); transaction.putState(key, state); } return state; } /** * @see org.geotools.data.simple.SimpleFeatureStore#modifyFeatures(java.lang.String, * java.lang.Object, org.opengis.filter.Filter) * @see #modifyFeatures(Name[], Object[], Filter) */ @Override public void modifyFeatures(String name, Object attributeValue, Filter filter) throws IOException { Name attributeName = new NameImpl(name); modifyFeatures(attributeName, attributeValue, filter); } /** * @see org.geotools.data.simple.SimpleFeatureStore#modifyFeatures(java.lang.String[], * java.lang.Object[], org.opengis.filter.Filter) * @see #modifyFeatures(Name[], Object[], Filter) */ @Override public void modifyFeatures(String[] names, Object[] attributeValues, Filter filter) throws IOException { Name[] attributeNames = new Name[names.length]; for (int i = 0; i < names.length; i++) { String name = names[i]; attributeNames[i] = new NameImpl(name); } modifyFeatures(attributeNames, attributeValues, filter); } /** * @see org.geotools.data.FeatureStore#modifyFeatures(org.opengis.feature.type.AttributeDescriptor[], * java.lang.Object[], org.opengis.filter.Filter) * @see #modifyFeatures(Name[], Object[], Filter) */ @Override public void modifyFeatures(AttributeDescriptor[] type, Object[] value, Filter filter) throws IOException { Name[] attributeNames = new Name[type.length]; for (int i = 0; i < type.length; i++) { attributeNames[i] = type[i].getName(); } modifyFeatures(attributeNames, value, filter); } /** * @see org.geotools.data.FeatureStore#modifyFeatures(org.opengis.feature.type.Name, * java.lang.Object, org.opengis.filter.Filter) * @see #modifyFeatures(Name[], Object[], Filter) */ @Override public void modifyFeatures(Name attributeName, Object attributeValue, Filter filter) throws IOException { modifyFeatures(new Name[] { attributeName }, new Object[] { attributeValue }, filter); } /** * @see org.geotools.data.FeatureStore#modifyFeatures(org.opengis.feature.type.AttributeDescriptor, * java.lang.Object, org.opengis.filter.Filter) * @see #modifyFeatures(Name[], Object[], Filter) */ @Override public void modifyFeatures(AttributeDescriptor type, Object value, Filter filter) throws IOException { modifyFeatures(new Name[] { type.getName() }, new Object[] { value }, filter); } }