org.geoserver.opensearch.eo.store.JDBCOpenSearchAccess.java Source code

Java tutorial

Introduction

Here is the source code for org.geoserver.opensearch.eo.store.JDBCOpenSearchAccess.java

Source

/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
 * This code is licensed under the GPL 2.0 license, available at the root
 * application directory.
 */
package org.geoserver.opensearch.eo.store;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.lang.StringUtils;
import org.geotools.data.DataStore;
import org.geotools.data.FeatureSource;
import org.geotools.data.Repository;
import org.geotools.data.ServiceInfo;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.AttributeTypeBuilder;
import org.geotools.feature.NameImpl;
import org.geotools.feature.TypeBuilder;
import org.opengis.feature.Feature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.AttributeType;
import org.opengis.feature.type.FeatureType;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.feature.type.GeometryType;
import org.opengis.feature.type.Name;

/**
 * A data store building OpenSearch for EO records based on a wrapped data store providing all expected tables in form of simple features (and
 * leveraging joins to put them together into complex features as needed).
 * 
 * The delegate store is fetched on demand to avoid being caught in a ResourcePool dispose
 *
 * @author Andrea Aime - GeoSolutions
 */
public class JDBCOpenSearchAccess implements OpenSearchAccess {

    public static final String COLLECTION = "collection";

    public static final String PRODUCT = "product";

    static final String EO_PREFIX = "eo";

    static final String SAR_PREFIX = "sar";

    static final String SOURCE_ATTRIBUTE = "sourceAttribute";

    Repository repository;

    Name delegateStoreName;

    String namespaceURI;

    FeatureType collectionFeatureType;

    FeatureType productFeatureType;

    List<Name> typeNames;

    public JDBCOpenSearchAccess(Repository repository, Name delegateStoreName, String namespaceURI)
            throws IOException {
        // TODO: maybe get a direct Catalog reference so that we can lookup by store id, which is
        // stable though renames?
        this.repository = repository;
        this.delegateStoreName = delegateStoreName;
        this.namespaceURI = namespaceURI;

        // check the expected feature types are available
        DataStore delegate = getDelegateStore();
        List<String> missingTables = getMissingRequiredTables(delegate, COLLECTION, PRODUCT);
        if (!missingTables.isEmpty()) {
            throw new IOException("Missing required tables in the backing store " + missingTables);
        }

        collectionFeatureType = buildCollectionFeatureType(delegate);
        productFeatureType = buildProductFeatureType(delegate);
    }

    private FeatureType buildCollectionFeatureType(DataStore delegate) throws IOException {
        SimpleFeatureType flatSchema = delegate.getSchema(COLLECTION);

        TypeBuilder typeBuilder = new TypeBuilder(CommonFactoryFinder.getFeatureTypeFactory(null));

        // map the source attributes
        for (AttributeDescriptor ad : flatSchema.getAttributeDescriptors()) {
            AttributeTypeBuilder ab = new AttributeTypeBuilder();
            String name = ad.getLocalName();
            String namespaceURI = this.namespaceURI;
            if (name.startsWith(EO_PREFIX)) {
                name = name.substring(EO_PREFIX.length());
                char c[] = name.toCharArray();
                c[0] = Character.toLowerCase(c[0]);
                name = new String(c);
                namespaceURI = EO_NAMESPACE;
            }
            // get a more predictable name structure (will have to do something for oracle
            // like names too I guess)
            if (StringUtils.isAllUpperCase(name)) {
                name = name.toLowerCase();
            }
            // map into output type
            ab.init(ad);
            ab.name(name).namespaceURI(namespaceURI).userData(SOURCE_ATTRIBUTE, ad.getLocalName());
            AttributeDescriptor mappedDescriptor;
            if (ad instanceof GeometryDescriptor) {
                GeometryType at = ab.buildGeometryType();
                ab.setCRS(((GeometryDescriptor) ad).getCoordinateReferenceSystem());
                mappedDescriptor = ab.buildDescriptor(new NameImpl(namespaceURI, name), at);
            } else {
                AttributeType at = ab.buildType();
                mappedDescriptor = ab.buildDescriptor(new NameImpl(namespaceURI, name), at);
            }

            typeBuilder.add(mappedDescriptor);
        }

        // adding the metadata property
        AttributeDescriptor metadataDescriptor = buildSimpleDescriptor(METADATA_PROPERTY_NAME, String.class);
        typeBuilder.add(metadataDescriptor);

        // map OGC links
        AttributeDescriptor linksDescriptor = buildFeatureListDescriptor(OGC_LINKS_PROPERTY_NAME,
                delegate.getSchema("collection_ogclink"));
        typeBuilder.add(linksDescriptor);

        typeBuilder.setName(COLLECTION);
        typeBuilder.setNamespaceURI(namespaceURI);
        return typeBuilder.feature();
    }

    private AttributeDescriptor buildSimpleDescriptor(Name name, Class binding) {
        AttributeTypeBuilder ab = new AttributeTypeBuilder();
        ab.name(name.getLocalPart()).namespaceURI(name.getNamespaceURI());
        ab.setBinding(String.class);
        AttributeDescriptor descriptor = ab.buildDescriptor(name, ab.buildType());
        return descriptor;
    }

    private AttributeDescriptor buildFeatureListDescriptor(Name name, SimpleFeatureType schema) {
        AttributeTypeBuilder ab = new AttributeTypeBuilder();
        ab.name(name.getLocalPart()).namespaceURI(name.getNamespaceURI());
        ab.setMinOccurs(0);
        ab.setMaxOccurs(Integer.MAX_VALUE);
        AttributeDescriptor descriptor = ab.buildDescriptor(name, schema);
        return descriptor;
    }

    private FeatureType buildProductFeatureType(DataStore delegate) throws IOException {
        SimpleFeatureType flatSchema = delegate.getSchema(PRODUCT);

        TypeBuilder typeBuilder = new TypeBuilder(CommonFactoryFinder.getFeatureTypeFactory(null));

        // map the source attributes
        AttributeTypeBuilder ab = new AttributeTypeBuilder();
        for (AttributeDescriptor ad : flatSchema.getAttributeDescriptors()) {
            String name = ad.getLocalName();
            String namespaceURI = this.namespaceURI;
            // hack to avoid changing the whole product attributes prefixes from eo to eop
            if (name.startsWith(EO_PREFIX)) {
                name = "eop" + name.substring(2);
            }
            for (ProductClass pc : ProductClass.values()) {
                String prefix = pc.getPrefix();
                if (name.startsWith(prefix)) {
                    name = name.substring(prefix.length());
                    char c[] = name.toCharArray();
                    c[0] = Character.toLowerCase(c[0]);
                    name = new String(c);
                    namespaceURI = pc.getNamespace();
                    break;
                }
            }

            // get a more predictable name structure (will have to do something for oracle
            // like names too I guess)
            if (StringUtils.isAllUpperCase(name)) {
                name = name.toLowerCase();
            }
            // map into output type
            ab.init(ad);
            ab.name(name).namespaceURI(namespaceURI).userData(SOURCE_ATTRIBUTE, ad.getLocalName());
            AttributeDescriptor mappedDescriptor;
            if (ad instanceof GeometryDescriptor) {
                GeometryType at = ab.buildGeometryType();
                ab.setCRS(((GeometryDescriptor) ad).getCoordinateReferenceSystem());
                mappedDescriptor = ab.buildDescriptor(new NameImpl(namespaceURI, name), at);
            } else {
                AttributeType at = ab.buildType();
                mappedDescriptor = ab.buildDescriptor(new NameImpl(namespaceURI, name), at);
            }

            typeBuilder.add(mappedDescriptor);
        }
        // adding the metadata property
        AttributeDescriptor metadataDescriptor = buildSimpleDescriptor(METADATA_PROPERTY_NAME, String.class);
        typeBuilder.add(metadataDescriptor);

        // adding the quicklook property
        AttributeDescriptor quicklookDescriptor = buildSimpleDescriptor(QUICKLOOK_PROPERTY_NAME, byte[].class);
        typeBuilder.add(quicklookDescriptor);

        // map OGC links
        AttributeDescriptor linksDescriptor = buildFeatureListDescriptor(OGC_LINKS_PROPERTY_NAME,
                delegate.getSchema("product_ogclink"));
        typeBuilder.add(linksDescriptor);

        typeBuilder.setName(PRODUCT);
        typeBuilder.setNamespaceURI(namespaceURI);
        return typeBuilder.feature();
    }

    private List<String> getMissingRequiredTables(DataStore delegate, String... tables) throws IOException {
        Set<String> availableNames = new HashSet<>(Arrays.asList(delegate.getTypeNames()));
        return Arrays.stream(tables).map(String::toLowerCase).filter(table -> !availableNames.contains(table))
                .collect(Collectors.toList());
    }

    /**
     * Returns the store from the repository (which is based on GeoServer own resource pool)
     * 
     * @return
     * @throws IOException
     */
    DataStore getDelegateStore() throws IOException {
        DataStore store = repository.dataStore(delegateStoreName);
        return new LowercasingDataStore(store);

    }

    @Override
    public ServiceInfo getInfo() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void createSchema(FeatureType featureType) throws IOException {
        throw new UnsupportedOperationException();
    }

    @Override
    public void updateSchema(Name typeName, FeatureType featureType) throws IOException {
        throw new UnsupportedOperationException();
    }

    @Override
    public void removeSchema(Name typeName) throws IOException {
        throw new UnsupportedOperationException();
    }

    @Override
    public List<Name> getNames() throws IOException {
        return Arrays.asList(collectionFeatureType.getName(), productFeatureType.getName());
    }

    @Override
    public FeatureType getSchema(Name name) throws IOException {
        for (FeatureType ft : Arrays.asList(collectionFeatureType, productFeatureType)) {
            if (name.equals(ft.getName())) {
                return ft;
            }
        }
        return null;
    }

    @Override
    public FeatureSource<FeatureType, Feature> getFeatureSource(Name typeName) throws IOException {
        if (collectionFeatureType.getName().equals(typeName)) {
            return getCollectionSource();
        } else if (productFeatureType.getName().equals(typeName)) {
            return getProductSource();
        }
        return null;

    }

    public FeatureSource<FeatureType, Feature> getProductSource() throws IOException {
        return new JDBCProductFeatureSource(this, productFeatureType);
    }

    public FeatureSource<FeatureType, Feature> getCollectionSource() throws IOException {
        return new JDBCCollectionFeatureSource(this, collectionFeatureType);
    }

    @Override
    public void dispose() {
        // nothing to dispose, the delegate store is managed by the resource pool
    }

}