Skip to content

Commit

Permalink
Add ability to publish data belonging to all DB schemas
Browse files Browse the repository at this point in the history
  • Loading branch information
groldan committed Aug 25, 2024
1 parent 554391b commit d199177
Show file tree
Hide file tree
Showing 28 changed files with 1,469 additions and 237 deletions.
5 changes: 5 additions & 0 deletions src/services/ogc-features/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</path>
<path>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>3.3.1</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.camptocamp.geotools.data.decorate;

import org.geotools.api.data.DataStore;

import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Delegate;

@RequiredArgsConstructor
public abstract class DecoratingDataStore implements DataStore {

@Delegate
protected final @NonNull DataStore delegate;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.camptocamp.geotools.data.decorate;

import org.geotools.api.data.SimpleFeatureSource;

import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Delegate;

@RequiredArgsConstructor
public class DecoratingFeatureSource implements SimpleFeatureSource {

@Delegate
protected final @NonNull SimpleFeatureSource delegate;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.camptocamp.geotools.data.decorate;

import static com.camptocamp.geotools.data.decorate.ReadOnlyDataStore.readOnlyException;

import org.geotools.api.data.DataStore;
import org.geotools.api.data.FeatureWriter;
import org.geotools.api.data.Transaction;
import org.geotools.api.feature.simple.SimpleFeature;
import org.geotools.api.feature.simple.SimpleFeatureType;
import org.geotools.api.feature.type.Name;
import org.geotools.api.filter.Filter;

import lombok.NonNull;

public abstract class DecoratingReadOnlyDataStore extends DecoratingDataStore implements ReadOnlyDataStore {

protected DecoratingReadOnlyDataStore(@NonNull DataStore delegate) {
super(delegate);
}

@Override
public FeatureWriter<SimpleFeatureType, SimpleFeature> getFeatureWriter(String typeName, Filter filter,
Transaction transaction) {
throw readOnlyException();
}

@Override
public FeatureWriter<SimpleFeatureType, SimpleFeature> getFeatureWriter(String typeName, Transaction transaction) {
throw readOnlyException();
}

@Override
public FeatureWriter<SimpleFeatureType, SimpleFeature> getFeatureWriterAppend(String typeName,
Transaction transaction) {
throw readOnlyException();
}

@Override
public void createSchema(SimpleFeatureType featureType) {
throw readOnlyException();
}

@Override
public void updateSchema(Name typeName, SimpleFeatureType featureType) {
throw readOnlyException();
}

@Override
public void removeSchema(Name typeName) {
throw readOnlyException();
}

@Override
public void updateSchema(String typeName, SimpleFeatureType featureType) {
throw readOnlyException();
}

@Override
public void removeSchema(String typeName) {
throw readOnlyException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.camptocamp.geotools.data.decorate;

import static org.springframework.util.ObjectUtils.isEmpty;

import java.util.function.Supplier;
import java.util.function.UnaryOperator;

import org.geotools.api.data.DataStore;

import lombok.NonNull;

public class PrefixingDataStore extends RenamingDataStore {

public PrefixingDataStore(@NonNull DataStore delegate, @NonNull Supplier</* Nullable */String> typeNamePrefix) {
super(delegate, rename(typeNamePrefix), undo(typeNamePrefix));
}

private static @NonNull UnaryOperator<String> rename(Supplier<String> typeNamePrefix) {
return name -> isEmpty(typeNamePrefix.get()) ? name : "%s%s".formatted(typeNamePrefix.get(), name);
}

private static @NonNull UnaryOperator<String> undo(Supplier<String> typeNamePrefix) {
return prefixedName -> isEmpty(typeNamePrefix.get()) ? prefixedName
: prefixedName.substring(typeNamePrefix.get().length());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.camptocamp.geotools.data.decorate;

import org.geotools.api.data.DataStore;
import org.geotools.api.data.FeatureWriter;
import org.geotools.api.data.LockingManager;
import org.geotools.api.data.Transaction;
import org.geotools.api.feature.simple.SimpleFeature;
import org.geotools.api.feature.simple.SimpleFeatureType;
import org.geotools.api.feature.type.Name;
import org.geotools.api.filter.Filter;
import org.geotools.data.InProcessLockingManager;

public interface ReadOnlyDataStore extends DataStore {

LockingManager IN_PROCESS_LOCKING = new InProcessLockingManager();

@Override
default LockingManager getLockingManager() {
return IN_PROCESS_LOCKING;
}

@Override
default FeatureWriter<SimpleFeatureType, SimpleFeature> getFeatureWriter(String typeName, Filter filter,
Transaction transaction) {
throw readOnlyException();
}

@Override
default FeatureWriter<SimpleFeatureType, SimpleFeature> getFeatureWriter(String typeName, Transaction transaction) {
throw readOnlyException();
}

@Override
default FeatureWriter<SimpleFeatureType, SimpleFeature> getFeatureWriterAppend(String typeName,
Transaction transaction) {
throw readOnlyException();
}

@Override
default void createSchema(SimpleFeatureType featureType) {
throw readOnlyException();
}

@Override
default void updateSchema(Name typeName, SimpleFeatureType featureType) {
throw readOnlyException();
}

@Override
default void removeSchema(Name typeName) {
throw readOnlyException();
}

@Override
default void updateSchema(String typeName, SimpleFeatureType featureType) {
throw readOnlyException();
}

@Override
default void removeSchema(String typeName) {
throw readOnlyException();
}

static UnsupportedOperationException readOnlyException() {
return new UnsupportedOperationException("read only");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.camptocamp.geotools.data.decorate;

import java.io.IOException;
import java.util.List;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;

import org.geotools.api.data.DataStore;
import org.geotools.api.data.FeatureReader;
import org.geotools.api.data.Query;
import org.geotools.api.data.SimpleFeatureSource;
import org.geotools.api.data.Transaction;
import org.geotools.api.feature.simple.SimpleFeature;
import org.geotools.api.feature.simple.SimpleFeatureType;
import org.geotools.api.feature.type.Name;
import org.geotools.data.ReTypeFeatureReader;
import org.geotools.feature.FeatureTypes;
import org.geotools.feature.NameImpl;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;

import lombok.NonNull;

/**
* Decorates a single {@link DataStore} to retype it's {@link FeatureTypes} by
* applying a transformation to the type name
*/
public class RenamingDataStore extends DecoratingReadOnlyDataStore {

private final UnaryOperator<String> rename;
private final UnaryOperator<String> undoRename;

public RenamingDataStore(@NonNull DataStore delegate, @NonNull UnaryOperator<String> rename,
@NonNull UnaryOperator<String> undoRename) {
super(delegate);
this.rename = rename;
this.undoRename = undoRename;
}

@Override
public String[] getTypeNames() throws IOException {
return Stream.of(super.getTypeNames()).map(rename).toArray(String[]::new);
}

@Override
public List<Name> getNames() throws IOException {
return super.getNames().stream().map(n -> new NameImpl(n.getNamespaceURI(), rename.apply(n.getLocalPart())))
.map(Name.class::cast).toList();
}

@Override
public SimpleFeatureType getSchema(Name name) throws IOException {
return getSchema(name.getLocalPart());
}

@Override
public SimpleFeatureType getSchema(String typeName) throws IOException {
final String origTypeName = undoRename.apply(typeName);
SimpleFeatureType featureType = super.getSchema(origTypeName);
if (!typeName.equals(origTypeName)) {
SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
builder.init(featureType);
builder.setName(typeName);
featureType = builder.buildFeatureType();
}
return featureType;
}

@Override
public SimpleFeatureSource getFeatureSource(String typeName) throws IOException {
String unprefixedTypeName = undoRename.apply(typeName);
var featureSource = super.getFeatureSource(unprefixedTypeName);
if (!typeName.equals(unprefixedTypeName)) {
featureSource = new RenamingFeatureSource(typeName, featureSource, this);
}
return featureSource;
}

@Override
public SimpleFeatureSource getFeatureSource(Name typeName) throws IOException {
return getFeatureSource(typeName.getLocalPart());
}

@Override
public FeatureReader<SimpleFeatureType, SimpleFeature> getFeatureReader(Query query, Transaction transaction)
throws IOException {
String typeName = query.getTypeName();
String unprefixedTypeName = undoRename.apply(typeName);

if (typeName.equals(unprefixedTypeName)) {
return super.getFeatureReader(query, transaction);
}
Query unprefixedQuery = new Query(query);
unprefixedQuery.setTypeName(unprefixedTypeName);
var reader = super.getFeatureReader(unprefixedQuery, transaction);
SimpleFeatureType prefixedSchema = getSchema(typeName);
return new ReTypeFeatureReader(reader, prefixedSchema);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.camptocamp.geotools.data.decorate;

import java.io.IOException;
import java.io.UncheckedIOException;

import org.geotools.api.data.DataAccess;
import org.geotools.api.data.Query;
import org.geotools.api.data.SimpleFeatureSource;
import org.geotools.api.feature.simple.SimpleFeature;
import org.geotools.api.feature.simple.SimpleFeatureType;
import org.geotools.api.feature.type.Name;
import org.geotools.api.filter.Filter;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.store.ReTypingFeatureCollection;
import org.geotools.geometry.jts.ReferencedEnvelope;

import lombok.NonNull;

class RenamingFeatureSource extends DecoratingFeatureSource implements SimpleFeatureSource {

private final String typeName;
private final RenamingDataStore renamingStore;

private SimpleFeatureType schema;

public RenamingFeatureSource(@NonNull String typeName, @NonNull SimpleFeatureSource delegate,
@NonNull RenamingDataStore renamingStore) {
super(delegate);
this.typeName = typeName;
this.renamingStore = renamingStore;
}

@Override
public DataAccess<SimpleFeatureType, SimpleFeature> getDataStore() {
return renamingStore;
}

@Override
public SimpleFeatureType getSchema() {
if (null == schema) {
try {
schema = renamingStore.getSchema(typeName);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
return schema;
}

@Override
public Name getName() {
return getSchema().getName();
}

@Override
public ReferencedEnvelope getBounds(Query query) throws IOException {
return delegate.getBounds(toSourceQuery(query));
}

@Override
public int getCount(Query query) throws IOException {
return delegate.getCount(toSourceQuery(query));
}

@Override
public SimpleFeatureCollection getFeatures() throws IOException {
return new ReTypingFeatureCollection(delegate.getFeatures(), getSchema());
}

@Override
public SimpleFeatureCollection getFeatures(Filter filter) throws IOException {
return new ReTypingFeatureCollection(delegate.getFeatures(filter), getSchema());
}

@Override
public SimpleFeatureCollection getFeatures(Query query) throws IOException {
return new ReTypingFeatureCollection(delegate.getFeatures(toSourceQuery(query)), getSchema());
}

Query toSourceQuery(Query query) {
Query q = new Query(query);
q.setTypeName(delegate.getName().getLocalPart());
return q;
}

}
Loading

0 comments on commit d199177

Please sign in to comment.