package mil.nga.giat.geowave.adapter.vector.plugin;
import java.awt.RenderingHints.Key;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFactorySpi;
import org.geotools.factory.FactoryIteratorProvider;
import org.geotools.factory.GeoTools;
import com.google.common.base.Function;
import com.google.common.collect.Iterators;
import mil.nga.giat.geowave.core.store.GeoWaveStoreFinder;
import mil.nga.giat.geowave.core.store.StoreFactoryFamilySpi;
/**
* This factory is injected by GeoTools using Java SPI and is used to expose
* GeoWave as a DataStore to GeoTools. It should be defined within a file
* META-INF/services/org.geotools.data.DataStoreFactorySpi to inject this into
* GeoTools.
*
*/
public class GeoWaveGTDataStoreFactory implements
DataStoreFactorySpi
{
private static class DataStoreCacheEntry
{
private final Map<String, Serializable> params;
private final DataStore dataStore;
public DataStoreCacheEntry(
final Map<String, Serializable> params,
final DataStore dataStore ) {
this.params = params;
this.dataStore = dataStore;
}
}
public final static String DISPLAY_NAME_PREFIX = "GeoWave Datastore - ";
private static final Logger LOGGER = LoggerFactory.getLogger(GeoWaveGTDataStoreFactory.class);
private final List<DataStoreCacheEntry> dataStoreCache = new ArrayList<DataStoreCacheEntry>();
private final StoreFactoryFamilySpi geowaveStoreFactoryFamily;
private static Boolean isAvailable = null;
/**
* Public "no argument" constructor called by Factory Service Provider (SPI)
* entry listed in META-INF/services/org.geotools.data.DataStoreFactorySPI
*/
public GeoWaveGTDataStoreFactory() {
final Collection<StoreFactoryFamilySpi> dataStoreFactories = GeoWaveStoreFinder
.getRegisteredStoreFactoryFamilies()
.values();
if (dataStoreFactories.isEmpty()) {
LOGGER.error("No GeoWave DataStore found! Geotools datastore for GeoWave is unavailable");
geowaveStoreFactoryFamily = null;
}
else {
final Iterator<StoreFactoryFamilySpi> it = dataStoreFactories.iterator();
geowaveStoreFactoryFamily = it.next();
if (it.hasNext()) {
GeoTools.addFactoryIteratorProvider(new GeoWaveGTDataStoreFactoryIteratorProvider());
}
}
}
public GeoWaveGTDataStoreFactory(
final StoreFactoryFamilySpi geowaveStoreFactoryFamily ) {
this.geowaveStoreFactoryFamily = geowaveStoreFactoryFamily;
}
// GeoServer seems to call this several times so we should cache a
// connection if the parameters are the same, I'm not sure this is entirely
// correct but it keeps us from making several connections for the same data
// store
@Override
public DataStore createDataStore(
final Map<String, Serializable> params )
throws IOException {
// iterate in reverse over the cache so the most recently added is
// accessed first
for (int index = dataStoreCache.size() - 1; index >= 0; index--) {
final DataStoreCacheEntry cacheEntry = dataStoreCache.get(index);
if (paramsEqual(
params,
cacheEntry.params)) {
return cacheEntry.dataStore;
}
}
return createNewDataStore(params);
}
private boolean paramsEqual(
final Map<String, Serializable> params1,
final Map<String, Serializable> params2 ) {
if (params1.size() == params2.size()) {
for (final Entry<String, Serializable> entry : params1.entrySet()) {
final Serializable value = params2.get(entry.getKey());
if (value == null) {
if (entry.getValue() == null) {
continue;
}
return false;
}
else if (!value.equals(entry.getValue())) {
return false;
}
}
return true;
}
return false;
}
@Override
public DataStore createNewDataStore(
final Map<String, Serializable> params )
throws IOException {
final GeoWaveGTDataStore dataStore;
try {
dataStore = new GeoWaveGTDataStore(
new GeoWavePluginConfig(
geowaveStoreFactoryFamily,
params));
dataStoreCache.add(new DataStoreCacheEntry(
params,
dataStore));
}
catch (final Exception ex) {
throw new IOException(
"Error initializing datastore",
ex);
}
return dataStore;
}
@Override
public String getDisplayName() {
return DISPLAY_NAME_PREFIX + geowaveStoreFactoryFamily.getType().toUpperCase();
}
@Override
public String getDescription() {
return "A datastore that uses the GeoWave API for spatial data persistence in "
+ geowaveStoreFactoryFamily.getType() + ". " + geowaveStoreFactoryFamily.getDescription();
}
@Override
public Param[] getParametersInfo() {
final List<Param> params = GeoWavePluginConfig.getPluginParams(geowaveStoreFactoryFamily);
return params.toArray(new Param[params.size()]);
}
@Override
public boolean canProcess(
final Map<String, Serializable> params ) {
final Map<String, String> dataStoreParams = params
.entrySet()
.stream()
.filter(
e -> !GeoWavePluginConfig.BASE_GEOWAVE_PLUGIN_PARAM_KEYS.contains(
e.getKey()))
.collect(
Collectors.toMap(
e -> e.getKey() == null ? null : e.getKey().toString(),
e -> e.getValue() == null ? null : e.getValue().toString()));
return GeoWaveStoreFinder.exactMatch(
geowaveStoreFactoryFamily,
dataStoreParams);
}
@Override
public synchronized boolean isAvailable() {
if (isAvailable == null) {
if (geowaveStoreFactoryFamily == null) {
isAvailable = false;
}
else {
try {
Class.forName("mil.nga.giat.geowave.adapter.vector.plugin.GeoWaveGTDataStore");
isAvailable = true;
}
catch (final ClassNotFoundException e) {
isAvailable = false;
}
}
}
return isAvailable;
}
@Override
public Map<Key, ?> getImplementationHints() {
// No implementation hints required at this time
return Collections.emptyMap();
}
private static class GeoWaveGTDataStoreFactoryIteratorProvider implements
FactoryIteratorProvider
{
@Override
public <T> Iterator<T> iterator(
final Class<T> cls ) {
if ((cls != null) && cls.isAssignableFrom(DataStoreFactorySpi.class)) {
return (Iterator<T>) new GeoWaveGTDataStoreFactoryIterator();
}
return null;
}
private static class GeoWaveGTDataStoreFactoryIterator implements
Iterator<DataStoreFactorySpi>
{
private final Iterator<DataStoreFactorySpi> it;
private GeoWaveGTDataStoreFactoryIterator() {
final Iterator<StoreFactoryFamilySpi> geowaveDataStoreIt = GeoWaveStoreFinder
.getRegisteredStoreFactoryFamilies()
.values()
.iterator();
geowaveDataStoreIt.next();
it = Iterators.transform(
geowaveDataStoreIt,
new GeoWaveStoreToGeoToolsDataStore());
}
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public DataStoreFactorySpi next() {
return it.next();
}
@Override
public void remove() {}
}
}
/**
* Below is a set of 9 additional GeoWaveGTDataStoreFactory's, its a bit of
* a hack, but must be done because the geotools factory registry will
* re-use instances of the same class, so each individual geowave data store
* must be registered as a different class (the alternative is dynamic
* compilation of classes to add to the classloader).
*
*
*/
private static class GeoWaveStoreToGeoToolsDataStore implements
Function<StoreFactoryFamilySpi, DataStoreFactorySpi>
{
private int i = 0;
public GeoWaveStoreToGeoToolsDataStore() {}
@Override
public DataStoreFactorySpi apply(
final StoreFactoryFamilySpi input ) {
i++;
switch (i) {
case 1:
return new GeoWaveGTDataStoreFactory1(
input);
case 2:
return new GeoWaveGTDataStoreFactory2(
input);
case 3:
return new GeoWaveGTDataStoreFactory3(
input);
case 4:
return new GeoWaveGTDataStoreFactory4(
input);
case 5:
return new GeoWaveGTDataStoreFactory5(
input);
case 6:
return new GeoWaveGTDataStoreFactory6(
input);
case 7:
return new GeoWaveGTDataStoreFactory7(
input);
case 8:
return new GeoWaveGTDataStoreFactory8(
input);
case 9:
return new GeoWaveGTDataStoreFactory9(
input);
}
LOGGER.error("Too many GeoWave Datastores registered for GeoTools data store");
return new GeoWaveGTDataStoreFactory(
input);
}
}
private static class GeoWaveGTDataStoreFactory1 extends
GeoWaveGTDataStoreFactory
{
public GeoWaveGTDataStoreFactory1(
final StoreFactoryFamilySpi geowaveStoreFactoryFamily ) {
super(
geowaveStoreFactoryFamily);
}
}
private static class GeoWaveGTDataStoreFactory2 extends
GeoWaveGTDataStoreFactory
{
public GeoWaveGTDataStoreFactory2(
final StoreFactoryFamilySpi geowaveStoreFactoryFamily ) {
super(
geowaveStoreFactoryFamily);
}
}
private static class GeoWaveGTDataStoreFactory3 extends
GeoWaveGTDataStoreFactory
{
public GeoWaveGTDataStoreFactory3(
final StoreFactoryFamilySpi geowaveStoreFactoryFamily ) {
super(
geowaveStoreFactoryFamily);
}
}
private static class GeoWaveGTDataStoreFactory4 extends
GeoWaveGTDataStoreFactory
{
public GeoWaveGTDataStoreFactory4(
final StoreFactoryFamilySpi geowaveStoreFactoryFamily ) {
super(
geowaveStoreFactoryFamily);
}
}
private static class GeoWaveGTDataStoreFactory5 extends
GeoWaveGTDataStoreFactory
{
public GeoWaveGTDataStoreFactory5(
final StoreFactoryFamilySpi geowaveStoreFactoryFamily ) {
super(
geowaveStoreFactoryFamily);
}
}
private static class GeoWaveGTDataStoreFactory6 extends
GeoWaveGTDataStoreFactory
{
public GeoWaveGTDataStoreFactory6(
final StoreFactoryFamilySpi geowaveStoreFactoryFamily ) {
super(
geowaveStoreFactoryFamily);
}
}
private static class GeoWaveGTDataStoreFactory7 extends
GeoWaveGTDataStoreFactory
{
public GeoWaveGTDataStoreFactory7(
final StoreFactoryFamilySpi geowaveStoreFactoryFamily ) {
super(
geowaveStoreFactoryFamily);
}
}
private static class GeoWaveGTDataStoreFactory8 extends
GeoWaveGTDataStoreFactory
{
public GeoWaveGTDataStoreFactory8(
final StoreFactoryFamilySpi geowaveStoreFactoryFamily ) {
super(
geowaveStoreFactoryFamily);
}
}
private static class GeoWaveGTDataStoreFactory9 extends
GeoWaveGTDataStoreFactory
{
public GeoWaveGTDataStoreFactory9(
final StoreFactoryFamilySpi geowaveStoreFactoryFamily ) {
super(
geowaveStoreFactoryFamily);
}
}
}