package mil.nga.giat.geowave.adapter.vector.plugin; import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.geotools.data.FeatureListenerManager; import org.geotools.data.Query; import org.geotools.data.Transaction; import org.geotools.data.store.ContentDataStore; import org.geotools.data.store.ContentEntry; import org.geotools.data.store.ContentFeatureSource; import org.geotools.feature.NameImpl; import org.geotools.referencing.CRS; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.Name; import org.opengis.referencing.FactoryException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import mil.nga.giat.geowave.adapter.vector.FeatureDataAdapter; import mil.nga.giat.geowave.adapter.vector.GeotoolsFeatureDataAdapter; import mil.nga.giat.geowave.adapter.auth.AuthorizationSPI; import mil.nga.giat.geowave.adapter.vector.index.IndexQueryStrategySPI; import mil.nga.giat.geowave.adapter.vector.index.SimpleFeaturePrimaryIndexConfiguration; import mil.nga.giat.geowave.adapter.vector.plugin.lock.LockingManagement; import mil.nga.giat.geowave.adapter.vector.plugin.transaction.GeoWaveAutoCommitTransactionState; import mil.nga.giat.geowave.adapter.vector.plugin.transaction.GeoWaveTransactionManagementState; import mil.nga.giat.geowave.adapter.vector.plugin.transaction.GeoWaveTransactionState; import mil.nga.giat.geowave.adapter.vector.plugin.transaction.MemoryTransactionsAllocator; import mil.nga.giat.geowave.adapter.vector.plugin.transaction.TransactionsAllocator; import mil.nga.giat.geowave.adapter.vector.plugin.visibility.VisibilityManagementHelper; import mil.nga.giat.geowave.core.geotime.ingest.SpatialDimensionalityTypeProvider; import mil.nga.giat.geowave.core.geotime.store.dimension.LatitudeField; import mil.nga.giat.geowave.core.geotime.store.dimension.LongitudeField; import mil.nga.giat.geowave.core.geotime.store.dimension.TimeField; import mil.nga.giat.geowave.core.index.ByteArrayId; import mil.nga.giat.geowave.core.index.StringUtils; import mil.nga.giat.geowave.core.store.AdapterToIndexMapping; import mil.nga.giat.geowave.core.store.CloseableIterator; import mil.nga.giat.geowave.core.store.DataStore; import mil.nga.giat.geowave.core.store.adapter.AdapterIndexMappingStore; import mil.nga.giat.geowave.core.store.adapter.AdapterStore; import mil.nga.giat.geowave.core.store.adapter.DataAdapter; import mil.nga.giat.geowave.core.store.adapter.statistics.DataStatisticsStore; import mil.nga.giat.geowave.core.store.data.visibility.VisibilityManagement; import mil.nga.giat.geowave.core.store.dimension.NumericDimensionField; import mil.nga.giat.geowave.core.store.index.Index; import mil.nga.giat.geowave.core.store.index.IndexStore; import mil.nga.giat.geowave.core.store.index.PrimaryIndex; import mil.nga.giat.geowave.core.store.query.EverythingQuery; import mil.nga.giat.geowave.core.store.query.QueryOptions; public class GeoWaveGTDataStore extends ContentDataStore { /** Package logger */ private final static Logger LOGGER = LoggerFactory.getLogger(GeoWaveGTDataStore.class); public static final CoordinateReferenceSystem DEFAULT_CRS; static { try { DEFAULT_CRS = CRS.decode( "EPSG:4326", true); } catch (final FactoryException e) { LOGGER.error( "Unable to decode EPSG:4326 CRS", e); throw new RuntimeException( "Unable to initialize EPSG:4326 object", e); } } private FeatureListenerManager listenerManager = null; protected AdapterStore adapterStore; protected IndexStore indexStore; protected DataStatisticsStore dataStatisticsStore; protected DataStore dataStore; protected AdapterIndexMappingStore adapterIndexMappingStore; private final Map<String, PrimaryIndex[]> preferredIndexes = new ConcurrentHashMap<String, PrimaryIndex[]>(); private final VisibilityManagement<SimpleFeature> visibilityManagement = VisibilityManagementHelper .loadVisibilityManagement(); private final AuthorizationSPI authorizationSPI; private final IndexQueryStrategySPI indexQueryStrategy; private final URI featureNameSpaceURI; private int transactionBufferSize = 10000; private final TransactionsAllocator transactionsAllocator; public GeoWaveGTDataStore( final GeoWavePluginConfig config ) throws IOException { listenerManager = new FeatureListenerManager(); lockingManager = config.getLockingManagementFactory().createLockingManager( config); authorizationSPI = config.getAuthorizationFactory().create( config.getAuthorizationURL()); init(config); featureNameSpaceURI = config.getFeatureNamespace(); indexQueryStrategy = config.getIndexQueryStrategy(); transactionBufferSize = config.getTransactionBufferSize(); transactionsAllocator = new MemoryTransactionsAllocator(); } private void init( final GeoWavePluginConfig config ) { dataStore = config.getDataStore(); dataStatisticsStore = config.getDataStatisticsStore(); indexStore = config.getIndexStore(); adapterStore = config.getAdapterStore(); adapterIndexMappingStore = config.getAdapterIndexMappingStore(); } public AuthorizationSPI getAuthorizationSPI() { return authorizationSPI; } public FeatureListenerManager getListenerManager() { return listenerManager; } public IndexQueryStrategySPI getIndexQueryStrategy() { return indexQueryStrategy; } public DataStore getDataStore() { return dataStore; } public AdapterStore getAdapterStore() { return adapterStore; } public IndexStore getIndexStore() { return indexStore; } public DataStatisticsStore getDataStatisticsStore() { return dataStatisticsStore; } protected PrimaryIndex[] getIndicesForAdapter( final GeotoolsFeatureDataAdapter adapter ) { PrimaryIndex[] currentSelections = preferredIndexes.get(adapter.getFeatureType().getName().toString()); if (currentSelections != null) { return currentSelections; } final AdapterToIndexMapping adapterIndexMapping = adapterIndexMappingStore.getIndicesForAdapter(adapter .getAdapterId()); if (adapterIndexMapping != null && adapterIndexMapping.isNotEmpty()) { currentSelections = adapterIndexMapping.getIndices(this.indexStore); } else { currentSelections = getPreferredIndices(adapter); } preferredIndexes.put( adapter.getFeatureType().getName().toString(), currentSelections); return currentSelections; } @Override public void createSchema( final SimpleFeatureType featureType ) { if (featureType.getGeometryDescriptor() == null) { throw new UnsupportedOperationException( "Schema missing geometry"); } final FeatureDataAdapter adapter = new FeatureDataAdapter( featureType, visibilityManagement); if (featureNameSpaceURI != null) { adapter.setNamespace(featureNameSpaceURI.toString()); } adapterStore.addAdapter(adapter); } private GeotoolsFeatureDataAdapter getAdapter( final String typeName ) { final GeotoolsFeatureDataAdapter featureAdapter; final DataAdapter<?> adapter = adapterStore.getAdapter(new ByteArrayId( StringUtils.stringToBinary(typeName))); if ((adapter == null) || !(adapter instanceof GeotoolsFeatureDataAdapter)) { return null; } featureAdapter = (GeotoolsFeatureDataAdapter) adapter; if (featureNameSpaceURI != null) { if (adapter instanceof FeatureDataAdapter) { ((FeatureDataAdapter) featureAdapter).setNamespace(featureNameSpaceURI.toString()); } } return featureAdapter; } @Override protected List<Name> createTypeNames() throws IOException { final List<Name> names = new ArrayList<>(); final CloseableIterator<DataAdapter<?>> adapters = adapterStore.getAdapters(); while (adapters.hasNext()) { final DataAdapter<?> adapter = adapters.next(); if (adapter instanceof GeotoolsFeatureDataAdapter) { names.add(((GeotoolsFeatureDataAdapter) adapter).getFeatureType().getName()); } } adapters.close(); return names; } @Override public ContentFeatureSource getFeatureSource( final String typeName ) throws IOException { return getFeatureSource( typeName, Transaction.AUTO_COMMIT); } @Override public ContentFeatureSource getFeatureSource( final String typeName, final Transaction tx ) throws IOException { return super.getFeatureSource( new NameImpl( null, typeName), tx); } @Override public ContentFeatureSource getFeatureSource( final Name typeName, final Transaction tx ) throws IOException { return getFeatureSource( typeName.getLocalPart(), tx); } @Override public ContentFeatureSource getFeatureSource( final Name typeName ) throws IOException { return getFeatureSource( typeName.getLocalPart(), Transaction.AUTO_COMMIT); } @Override protected ContentFeatureSource createFeatureSource( final ContentEntry entry ) throws IOException { return new GeoWaveFeatureSource( entry, Query.ALL, getAdapter(entry.getTypeName()), transactionsAllocator); } @Override public void removeSchema( final Name typeName ) throws IOException { this.removeSchema(typeName.getLocalPart()); } @Override public void removeSchema( final String typeName ) throws IOException { final DataAdapter<?> adapter = adapterStore.getAdapter(new ByteArrayId( StringUtils.stringToBinary(typeName))); if (adapter != null) { final String[] authorizations = getAuthorizationSPI().getAuthorizations(); dataStore.delete( new QueryOptions( adapter, authorizations), new EverythingQuery()); // TODO do we want to delete the adapter from the adapter store? } } /** * Used to retrieve the TransactionStateDiff for this transaction. * <p> * * @param transaction * @return GeoWaveTransactionState or null if subclass is handling * differences * @throws IOException */ protected GeoWaveTransactionState getMyTransactionState( final Transaction transaction, final GeoWaveFeatureSource source ) throws IOException { synchronized (transaction) { GeoWaveTransactionState state = null; if (transaction == Transaction.AUTO_COMMIT) { state = new GeoWaveAutoCommitTransactionState( source); } else { state = (GeoWaveTransactionState) transaction.getState(this); if (state == null) { state = new GeoWaveTransactionManagementState( transactionBufferSize, source.getComponents(), transaction, (LockingManagement) lockingManager); transaction.putState( this, state); } } return state; } } public PrimaryIndex[] getPreferredIndices( final GeotoolsFeatureDataAdapter adapter ) { List<PrimaryIndex> currentSelectionsList = new ArrayList<PrimaryIndex>( 2); final List<String> indexNames = SimpleFeaturePrimaryIndexConfiguration.getIndexNames(adapter.getFeatureType()); final boolean canUseTime = adapter.hasTemporalConstraints(); /** * Requires the indices to EXIST prior to set up of the adapter. * Otherwise, only Geospatial is chosen and the index Names are ignored. */ try (CloseableIterator<Index<?, ?>> indices = indexStore.getIndices()) { while (indices.hasNext()) { Index<?, ?> nextIndex = indices.next(); if (!(nextIndex instanceof PrimaryIndex)) continue; final PrimaryIndex index = (PrimaryIndex) nextIndex; if (!indexNames.isEmpty()) { // Only used selected preferred indices if (indexNames.contains(index.getId().getString())) { currentSelectionsList.add(index); } } @SuppressWarnings("rawtypes") final NumericDimensionField[] dims = index.getIndexModel().getDimensions(); boolean hasLat = false; boolean hasLong = false; boolean hasTime = false; for (final NumericDimensionField<?> dim : dims) { hasLat |= dim instanceof LatitudeField; hasLong |= dim instanceof LongitudeField; hasTime |= dim instanceof TimeField; } if (hasLat && hasLong) { // If not requiring time OR (requires time AND has time // constraints) if (!hasTime || canUseTime) { currentSelectionsList.add(index); } } } } catch (final IOException ex) { LOGGER.error( "Cannot close index iterator.", ex); } if (currentSelectionsList.isEmpty()) currentSelectionsList.add(new SpatialDimensionalityTypeProvider().createPrimaryIndex()); return currentSelectionsList.toArray(new PrimaryIndex[currentSelectionsList.size()]); } }