/***************************************************
*
* cismet GmbH, Saarbruecken, Germany
*
* ... and it just works.
*
****************************************************/
package de.cismet.cismap.commons.featureservice.factory;
import org.apache.log4j.Logger;
import org.postgis.Geometry;
import org.postgis.PGgeometry;
import org.postgresql.PGConnection;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Vector;
import javax.swing.SwingWorker;
import de.cismet.cismap.commons.BoundingBox;
import de.cismet.cismap.commons.CrsTransformer;
import de.cismet.cismap.commons.features.PostgisFeature;
import de.cismet.cismap.commons.features.UpdateablePostgisFeature;
import de.cismet.cismap.commons.featureservice.FeatureServiceAttribute;
import de.cismet.cismap.commons.featureservice.LayerProperties;
import de.cismet.cismap.commons.featureservice.SimpleFeatureServiceSqlStatement;
import de.cismet.cismap.commons.interaction.CismapBroker;
import de.cismet.cismap.commons.jtsgeometryfactories.PostGisGeometryFactory;
import de.cismet.cismap.commons.retrieval.RetrievalService;
import de.cismet.tools.ConnectionInfo;
/**
* DOCUMENT ME!
*
* @version $Revision$, $Date$
*/
public class PostgisFeatureFactory extends AbstractFeatureFactory<PostgisFeature, SimpleFeatureServiceSqlStatement> {
//~ Static fields/initializers ---------------------------------------------
private static Logger logger = Logger.getLogger(PostgisFeatureFactory.class);
public static final String ID_TOKEN = "<cismap::update::id>";
public static final String QUERY_CANCELED = "57014";
//~ Instance fields --------------------------------------------------------
protected final ConnectionInfo connectionInfo;
protected final PostgisAction postgisAction;
protected final RetrievalService parentService;
private Connection connection;
//~ Constructors -----------------------------------------------------------
/**
* Creates a new PostgisFeatureFactory object.
*
* @param layerProperties DOCUMENT ME!
* @param connectionInfo DOCUMENT ME!
* @param postgisAction DOCUMENT ME!
* @param parentService DOCUMENT ME!
*
* @throws Exception DOCUMENT ME!
*/
public PostgisFeatureFactory(final LayerProperties layerProperties,
final ConnectionInfo connectionInfo,
final PostgisAction postgisAction,
final RetrievalService parentService) throws Exception {
// this.setLayerProperties(layerProperties);
this.layerProperties = layerProperties;
this.connectionInfo = connectionInfo;
this.postgisAction = postgisAction;
this.parentService = parentService;
this.connection = createConnection(connectionInfo);
}
/**
* Creates a new PostgisFeatureFactory object.
*
* @param pff DOCUMENT ME!
*/
protected PostgisFeatureFactory(final PostgisFeatureFactory pff) {
super(pff);
this.connectionInfo = pff.connectionInfo;
this.postgisAction = pff.postgisAction;
this.parentService = pff.parentService;
try {
this.connection = createConnection(connectionInfo);
} catch (Throwable t) {
logger.error("could not create connection: " + t.getMessage(), t);
}
}
//~ Methods ----------------------------------------------------------------
/**
* DOCUMENT ME!
*
* @param connectionInfo DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws Exception DOCUMENT ME!
*/
public static Connection createConnection(final ConnectionInfo connectionInfo) throws Exception {
try {
logger.info("creating new PostgisFeatureFactory instance with connection: connection: \n"
+ connectionInfo.getUrl() + ", " + connectionInfo.getDriver() + ", "
+ connectionInfo.getUser());
Class.forName(connectionInfo.getDriver());
final Connection theConnection = DriverManager.getConnection(connectionInfo.getUrl(),
connectionInfo.getUser(),
connectionInfo.getPass());
((PGConnection)theConnection).addDataType("geometry", "org.postgis.PGgeometry");
((PGConnection)theConnection).addDataType("box3d", "org.postgis.PGbox3d");
return theConnection;
} catch (Throwable t) {
logger.fatal("could not create database connection (" + connectionInfo + "):\n " + t.getMessage(), t);
throw new Exception("could not create database connection (" + connectionInfo + "):\n " + t.getMessage(),
t);
}
}
@Override
protected boolean isGenerateIds() {
return false;
}
@Override
public synchronized List<PostgisFeature> createFeatures(final SimpleFeatureServiceSqlStatement sqlStatement,
final BoundingBox boundingBox,
final SwingWorker workerThread) throws FeatureFactory.TooManyFeaturesException, Exception {
return createFeatures_internal(sqlStatement, boundingBox, workerThread, 0, 0, null, true);
}
@Override
public Vector createAttributes(final SwingWorker workerThread) throws FeatureFactory.TooManyFeaturesException,
Exception {
final Vector featureServiceAttributes = new Vector(4);
featureServiceAttributes.add(new FeatureServiceAttribute(
PostgisFeature.GEO_PROPERTY,
"gml:GeometryPropertyType",
true));
featureServiceAttributes.add(new FeatureServiceAttribute(PostgisFeature.ID_PROPERTY, "1", true));
featureServiceAttributes.add(new FeatureServiceAttribute(PostgisFeature.FEATURE_TYPE_PROPERTY, "2", true));
featureServiceAttributes.add(new FeatureServiceAttribute(PostgisFeature.GROUPING_KEY_PROPERTY, "2", true));
featureServiceAttributes.add(new FeatureServiceAttribute(PostgisFeature.OBJECT_NAME_PROPERTY, "2", true));
return featureServiceAttributes;
}
/**
* DOCUMENT ME!
*
* @param statement DOCUMENT ME!
*/
protected void cleanup(Statement statement) {
if (statement == null) {
return;
}
try {
statement.cancel();
statement.close();
statement = null;
} catch (Exception ex) {
}
}
@Override
public PostgisFeatureFactory clone() {
return new PostgisFeatureFactory(this);
}
@Override
public int getFeatureCount(final SimpleFeatureServiceSqlStatement query, final BoundingBox bb) {
return 0;
}
@Override
public synchronized List<PostgisFeature> createFeatures(final SimpleFeatureServiceSqlStatement sqlStatement,
final BoundingBox boundingBox,
final SwingWorker workerThread,
final int offset,
final int limit,
final FeatureServiceAttribute[] orderBy) throws TooManyFeaturesException, Exception {
return createFeatures_internal(sqlStatement, boundingBox, workerThread, offset, limit, orderBy, false);
}
/**
* DOCUMENT ME!
*
* @param sqlStatement DOCUMENT ME!
* @param boundingBox DOCUMENT ME!
* @param workerThread DOCUMENT ME!
* @param offset DOCUMENT ME!
* @param limit DOCUMENT ME!
* @param orderBy DOCUMENT ME!
* @param saveAsLastCreated DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws TooManyFeaturesException DOCUMENT ME!
* @throws Exception DOCUMENT ME!
*/
private synchronized List<PostgisFeature> createFeatures_internal(
final SimpleFeatureServiceSqlStatement sqlStatement,
final BoundingBox boundingBox,
final SwingWorker workerThread,
final int offset,
final int limit,
final FeatureServiceAttribute[] orderBy,
final boolean saveAsLastCreated) throws TooManyFeaturesException, Exception {
if (checkCancelled(workerThread, "createFeatures()")) {
return null;
}
Statement statement = null;
Vector postgisFeatures = null;
final long start = System.currentTimeMillis();
try {
if ((this.connection == null) || (this.connection.isClosed())) {
this.logger.error("FRW[" + workerThread
+ "]: Connection to database lost or not correctly initialised");
this.connection = createConnection(this.connectionInfo);
}
sqlStatement.setX1(boundingBox.getX1());
sqlStatement.setX2(boundingBox.getX2());
sqlStatement.setY1(boundingBox.getY1());
sqlStatement.setY2(boundingBox.getY2());
if (checkCancelled(workerThread, "initialising sql statement()")) {
cleanup(statement);
return null;
}
statement = this.connection.createStatement();
if (this.logger.isDebugEnabled()) {
this.logger.debug("FRW[" + workerThread + "]: executing count features statement: "
+ sqlStatement.getCountFeaturesStatement());
}
ResultSet resultSet = statement.executeQuery(sqlStatement.getCountFeaturesStatement());
if (checkCancelled(workerThread, "initialising sql statement()")) {
cleanup(statement);
return null;
}
int count = 0;
if (resultSet.next()) {
count = resultSet.getInt(1);
}
resultSet.close();
if (this.logger.isDebugEnabled()) {
this.logger.debug("FRW[" + workerThread + "]: " + count
+ " matching features in selected bounding box");
}
if (count > getMaxFeatureCount()) {
throw new FeatureFactory.TooManyFeaturesException("FRW[" + workerThread
+ "]: feature in feature document " + count + " exceeds max feature count "
+ getMaxFeatureCount());
}
if (count == 0) {
this.logger.warn("FRW[" + workerThread + "]: no features found in selected bounding ");
return null;
}
this.logger.info("FRW[" + workerThread + "]: " + count + " postgis features found by sql statement");
if (checkCancelled(workerThread, " counting postgis features")) {
cleanup(statement);
return null;
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("FRW[" + workerThread + "]: executing select features statement: "
+ sqlStatement.getFeaturesStatement());
}
resultSet = statement.executeQuery(sqlStatement.getFeaturesStatement());
postgisFeatures = new Vector(count);
int j = 0;
while (resultSet.next()) {
if (checkCancelled(workerThread, " processing postgis feature #" + count)) {
cleanup(statement);
return null;
}
String name = "";
try {
name = resultSet.getObject(PostgisFeature.OBJECT_NAME_PROPERTY).toString();
} catch (Exception e) {
if (DEBUG) {
logger.warn("FRW[" + workerThread + "]: name is null");
}
}
String type = "";
try {
type = resultSet.getObject(PostgisFeature.FEATURE_TYPE_PROPERTY).toString();
} catch (Exception e) {
if (DEBUG) {
logger.warn("FRW[" + workerThread + "]: type is null");
}
}
String groupingKey = "";
try {
groupingKey = resultSet.getObject(PostgisFeature.GROUPING_KEY_PROPERTY).toString();
} catch (Exception e) {
if (DEBUG) {
logger.warn("FRW[" + workerThread + "]: GroupingKey is null");
}
}
int id = -1;
try {
id = resultSet.getInt(PostgisFeature.ID_PROPERTY);
} catch (Exception e) {
logger.warn("FRW[" + workerThread + "]: Id is null", e);
if (DEBUG) {
logger.warn("FRW[" + workerThread + "]: Id is null");
}
}
final PGgeometry postgresGeom = (PGgeometry)resultSet.getObject(PostgisFeature.GEO_PROPERTY);
final Geometry postgisGeom = postgresGeom.getGeometry();
PostgisFeature postgisFeature;
if (this.postgisAction != null) {
postgisFeature = new UpdateablePostgisFeature(
connectionInfo,
parentService,
postgisAction,
connection);
} else {
postgisFeature = new PostgisFeature();
}
postgisFeature.setId(id);
postgisFeature.setGeometry(PostGisGeometryFactory.createJtsGeometry(postgisGeom));
postgisFeature.setFeatureType(type);
postgisFeature.setGroupingKey(groupingKey);
postgisFeature.setObjectName(name);
postgisFeature.setLayerProperties(getLayerProperties());
evaluateExpressions(postgisFeature, j);
postgisFeatures.add(postgisFeature);
++j;
}
} catch (Exception e) {
final SQLException se;
this.logger.error("FRW[" + workerThread + "]: Exception during Postgis Featureretrieval: \n"
+ e.getMessage(),
e);
if (e instanceof SQLException) {
se = (SQLException)e;
}
throw e;
} finally {
cleanup(statement);
}
this.logger.info("FRW[" + workerThread + "]: Postgis request took " + (System.currentTimeMillis() - start)
+ " ms");
if (saveAsLastCreated) {
final int crs = CrsTransformer.extractSridFromCrs(CismapBroker.getInstance().getSrs().getCode());
updateLastCreatedFeatures(postgisFeatures, boundingBox.getGeometry(crs), sqlStatement);
}
return postgisFeatures;
}
}