package au.org.aurin.wif.io;
import static org.geotools.jdbc.JDBCDataStoreFactory.DATABASE;
import static org.geotools.jdbc.JDBCDataStoreFactory.HOST;
import static org.geotools.jdbc.JDBCDataStoreFactory.SCHEMA;
import java.io.File;
import java.io.IOException;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import org.apache.commons.io.FileUtils;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.Transaction;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import au.org.aurin.wif.config.WifConfig;
import au.org.aurin.wif.exception.io.DataStoreCreationException;
import au.org.aurin.wif.exception.io.DataStoreUnavailableException;
import au.org.aurin.wif.exception.io.ShapeFile2PostGISCreationException;
import au.org.aurin.wif.model.WifProject;
import au.org.aurin.wif.svc.WifKeys;
/**
* The Class DataStoreToPostgisExporter.
*/
@Component
public class DataStoreToPostgisExporter implements InitializingBean {
/** The Constant LOGGER. */
private static final Logger LOGGER = LoggerFactory
.getLogger(DataStoreToPostgisExporter.class);
/** The data store client. */
@Autowired
private DataStoreClient dataStoreClient;
/** The file to postgis exporter. */
@Autowired
private FileToPostgisExporter fileToPostgisExporter;
/** The wif config. */
@Resource
private WifConfig wifConfig;
/** The postgis data store config. */
@Autowired
@Qualifier("wifDataStoreConfig")
private PostgisDataStoreConfig postgisDataStoreConfig;
// private GeospatialDataSource wifDataSource;
/**
* Inits the.
*/
@PostConstruct
public void init() {
LOGGER.trace("Initializing version: " + WifKeys.WIF_KEY_VERSION);
LOGGER.info("using the following server: {} for database/schema: {}",
postgisDataStoreConfig.getDataStoreParams().get(HOST.key),
postgisDataStoreConfig.getDataStoreParams().get(DATABASE.key) + "/"
+ postgisDataStoreConfig.getDataStoreParams().get(SCHEMA.key));
}
// TODO Why did I comment this??
// @PreDestroy
// public void cleanup() {
// if (wifDataSource != null) {
// if (wifDataSource.getDataStore() != null) {
// LOGGER.info(" Attempting to dispose of data store ");
// wifDataSource.getDataStore().dispose();
// }
// }
// LOGGER.info(" Service succesfully cleared! ");
// }
/**
* Sets the data store client.
*
* @param dataStoreClient
* the new data store client
*/
public void setDataStoreClient(final DataStoreClient dataStoreClient) {
this.dataStoreClient = dataStoreClient;
}
/**
* Convert geo json to feature collection.
*
* @param owner
* the owner
* @param geoJsonUri
* the geo json uri
* @return the feature collection
* @throws DataStoreUnavailableException
* the data store unavailable exception
*/
public FeatureCollection<SimpleFeatureType, SimpleFeature> convertGeoJSONToFeatureCollection(
final String owner, final String geoJsonUri)
throws DataStoreUnavailableException {
LOGGER
.debug("Entering DataStoreToPostgisExporter.convertGeoJSONToFeatureCollection()");
LOGGER.info("exporting to postGIS, from datastore URI={}", geoJsonUri);
SslUtil.trustSelfSignedSSL();
FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollection = null;
final long startTime = System.nanoTime();
try {
featureCollection = dataStoreClient.getDataFeatureCollectionInGeoJSON(
owner, geoJsonUri);
} catch (final Exception e) {
final String msg = "DataStore for doc-id: " + geoJsonUri
+ " isn't available: " + e.getMessage();
LOGGER.error(msg);
throw new DataStoreUnavailableException(msg, e);
}
final long endTime = System.nanoTime();
if (featureCollection == null) {
final String msg = "featureCollection is null: ";
LOGGER.error(msg);
throw new DataStoreUnavailableException(msg);
}
logTime(startTime, endTime,
"Get GeoJSON object from DataStore to FeatureCollection took ");
return featureCollection;
}
/**
* Store feature collection to postgis.
*
* @param featureCollection
* the feature collection
* @param tableName
* the table name
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public void storeFeatureCollectionToPostgis(
final FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollection,
final String tableName) throws IOException {
LOGGER
.info("Entering DataStoreToPostgisExporter.storeFeatureCollectionToPostgis()");
LOGGER.info("using the following server: {} for database/schema: {}",
postgisDataStoreConfig.getDataStoreParams().get(HOST.key),
postgisDataStoreConfig.getDataStoreParams().get(DATABASE.key) + "/"
+ postgisDataStoreConfig.getDataStoreParams().get(SCHEMA.key));
long startTime = System.nanoTime();
final DataStore dataStore = DataStoreFinder
.getDataStore(postgisDataStoreConfig.getDataStoreParams());
long endTime = System.nanoTime();
logTime(startTime, endTime, "Accessing datastore ");
final SimpleFeatureType featureType = featureCollection.getSchema();
// here's one way for us to overwrite the defaut typeName geotools gives
// unnamed featureTypes
final SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
builder.init(featureType);
builder.setName(tableName);
final SimpleFeatureType newFeatureType = builder.buildFeatureType();
dataStore.createSchema(newFeatureType);
final Transaction transaction = new DefaultTransaction("create");
final String typeName = newFeatureType.getTypeName();
final SimpleFeatureSource featureSource = dataStore
.getFeatureSource(typeName);
if (featureSource instanceof SimpleFeatureStore) {
final SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
featureStore.setTransaction(transaction);
try {
LOGGER.debug(">>>>>>>>>>>>>>*************** Beginning addFeatures");
featureStore.addFeatures(featureCollection);
LOGGER.debug(">>>>>>>>>>>>>>*************** Beginning commit");
startTime = System.nanoTime();
transaction.commit();
endTime = System.nanoTime();
logTime(startTime, endTime,
"Transaction to all of features to DB took ");
} catch (final Exception problem) {
// problem.printStackTrace();
transaction.rollback();
LOGGER
.error("Problem occurred while storing the feature collection. We'll need to rollback...");
} finally {
transaction.close();
if (featureSource != null) {
if (featureSource.getDataStore() != null) {
LOGGER.info(" Attempting to dispose of postGIS data store ");
featureSource.getDataStore().dispose();
}
}
}
} else {
LOGGER.error(typeName + " table does not support read/write access");
throw new IOException(typeName
+ " table does not support read/write access");
}
if (featureSource != null) {
if (featureSource.getDataStore() != null) {
LOGGER.info(" Attempting to dispose of postGIS data store ");
featureSource.getDataStore().dispose();
}
}
}
/**
* Export user provided geospatial information to postGIS datastore. If
* standalone is enabled, we'll obtain the information from a previously
* unzipped shape file, otherwise it will get a geojson from AURIN datastore.
*
* @param owner
* the owner
* @param project
* the project
* @param tableName
* the table name
* @return the simple feature type
* @throws IOException
* Signals that an I/O exception has occurred.
* @throws DataStoreUnavailableException
* the data store unavailable exception
* @throws DataStoreCreationException
* the data store creation exception
* @throws ShapeFile2PostGISCreationException
* the shape file2 post gis creation exception
*/
public WifProject exportToPostgis(final String owner,
final WifProject project, final String tableName) throws IOException,
DataStoreUnavailableException, DataStoreCreationException,
ShapeFile2PostGISCreationException {
LOGGER.debug("Creating feature collection from owner={}", owner);
try {
FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollection = null;
File shpFile = null;
if (project.getLocalShpFile() == null) {
if (project.getUazDataStoreURI() == null) {
LOGGER.error("DataStoreURI needed to create the project");
throw new DataStoreCreationException(
"DataStoreURI needed to create the project");
}
featureCollection = convertGeoJSONToFeatureCollection(owner,
project.getUazDataStoreURI());
} else {
if (project.getLocalShpFile() != null
&& project.getUazDataStoreURI() == null) {
LOGGER.debug("...with local shape file ={}",
project.getLocalShpFile());
shpFile = new File(project.getLocalShpFile());
featureCollection = fileToPostgisExporter.zipFileToPostGIS(shpFile);
} else {
throw new DataStoreCreationException("LocalShpFile not set");
}
}
final ReferencedEnvelope bbox = featureCollection.getBounds();
final String bboxJson = new StringBuffer().append("[")
.append(bbox.getMinX()).append(",").append(bbox.getMinY())
.append(",").append(bbox.getMaxX()).append(",")
.append(bbox.getMaxY()).append("]").toString();
project.setBbox(bboxJson);
project.setSchema(featureCollection.getSchema());
storeFeatureCollectionToPostgis(featureCollection, tableName);
// Deleting the temporal file created
if (project.getLocalShpFile() != null) {
LOGGER.debug("deleting temporal local shape file ={}",
project.getLocalShpFile());
if (shpFile != null) {
if (FileUtils.deleteQuietly(shpFile)) {
LOGGER.info("{} has been deleted successfully.",
shpFile.getAbsolutePath());
// ali
final String fileNameWithOutExt = org.apache.commons.io.FilenameUtils
.removeExtension(shpFile.getName());
final String dPath = shpFile.getParentFile().getAbsolutePath();
final File f = new File(dPath + "/" + fileNameWithOutExt + ".dbf");
if (f.exists() && !f.isDirectory()) {
LOGGER.info("file deleted :" + f.getAbsolutePath());
f.delete();
}
final File f1 = new File(dPath + "/" + fileNameWithOutExt + ".shx");
if (f1.exists() && !f1.isDirectory()) {
LOGGER.info("file deleted :" + f1.getAbsolutePath());
f1.delete();
}
final File f2 = new File(dPath + "/" + fileNameWithOutExt + ".fix");
if (f2.exists() && !f2.isDirectory()) {
LOGGER.info("file deleted :" + f2.getAbsolutePath());
f2.delete();
}
final File f3 = new File(dPath + "/" + fileNameWithOutExt + ".prj");
if (f3.exists() && !f3.isDirectory()) {
LOGGER.info("file deleted :" + f3.getAbsolutePath());
f3.delete();
}
final File f4 = new File(dPath + "/" + fileNameWithOutExt + ".qix");
if (f4.exists() && !f4.isDirectory()) {
LOGGER.info("file deleted :" + f4.getAbsolutePath());
f4.delete();
}
} else {
LOGGER
.warn(
"{} delete operation failed. in the future the disc can possibly run out of space",
shpFile.getAbsolutePath());
}
}
}
return project;
} catch (final Exception e) {
final String msg = "Creating postGIS spatial store with table name "
+ tableName + " failed";
LOGGER.error(msg);
throw new DataStoreCreationException(msg, e);
}
}
/*
* (non-Javadoc)
* @see
* org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(dataStoreClient);
}
/**
* Log time.
*
* @param startTime
* the start time
* @param endTime
* the end time
* @param msg
* the msg
*/
public void logTime(final long startTime, final long endTime, final String msg) {
final long duration = endTime - startTime;
LOGGER.info(">>>>>>>>>>>>>>*************** " + msg + " in ms " + duration
/ WifKeys.TIME_LAPSE);
}
/**
* Gets the features from a given table in postGIS.
*
* @param tableName
* the table name
* @return the features
* @throws DataStoreCreationException
* the data store creation exception
*/
public FeatureCollection<SimpleFeatureType, SimpleFeature> getFeatures(
final String tableName) throws DataStoreCreationException {
LOGGER.info("Entering DataStoreToPostgisExporter.getFeatures()");
LOGGER.info("using the following server: {} for database/schema: {}",
postgisDataStoreConfig.getDataStoreParams().get(HOST.key),
postgisDataStoreConfig.getDataStoreParams().get(DATABASE.key) + "/"
+ postgisDataStoreConfig.getDataStoreParams().get(SCHEMA.key));
try {
final DataStore dataStore = DataStoreFinder
.getDataStore(postgisDataStoreConfig.getDataStoreParams());
return dataStore.getFeatureSource(tableName).getFeatures();
} catch (final IOException e) {
final String msg = "obtaining postGIS spatial features from table name "
+ tableName + " failed";
LOGGER.error(msg);
throw new DataStoreCreationException(msg, e);
}
}
}