/* * Constellation - An open source and standard compliant SDI * http://www.constellation-sdi.org * * Copyright 2014 Geomatys. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.constellation.data.om2; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.io.WKBReader; import com.vividsolutions.jts.io.WKBWriter; import org.apache.sis.storage.DataStoreException; import org.geotoolkit.data.AbstractFeatureStore; import org.geotoolkit.data.FeatureReader; import org.geotoolkit.data.FeatureStoreFactory; import org.geotoolkit.data.FeatureStoreFinder; import org.geotoolkit.data.FeatureStoreRuntimeException; import org.geotoolkit.data.FeatureWriter; import org.geotoolkit.data.query.DefaultQueryCapabilities; import org.geotoolkit.data.query.Query; import org.geotoolkit.data.query.QueryCapabilities; import org.geotoolkit.factory.Hints; import org.geotoolkit.feature.Feature; import org.geotoolkit.feature.FeatureFactory; import org.geotoolkit.feature.FeatureTypeBuilder; import org.geotoolkit.feature.Property; import org.geotoolkit.feature.simple.DefaultSimpleFeatureType; import org.geotoolkit.feature.type.AttributeDescriptor; import org.geotoolkit.feature.type.DefaultGeometryDescriptor; import org.geotoolkit.feature.type.FeatureType; import org.geotoolkit.feature.type.PropertyDescriptor; import org.geotoolkit.filter.identity.DefaultFeatureId; import org.geotoolkit.jdbc.ManageableDataSource; import org.geotoolkit.parameter.Parameters; import org.geotoolkit.referencing.CRS; import org.opengis.filter.Filter; import org.opengis.filter.identity.FeatureId; import org.opengis.parameter.ParameterValueGroup; import org.opengis.referencing.NoSuchAuthorityCodeException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.util.FactoryException; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import static org.constellation.data.om2.OM2FeatureStoreFactory.SCHEMA_PREFIX; import static org.constellation.data.om2.OM2FeatureStoreFactory.SGBDTYPE; import org.geotoolkit.util.NamesExt; import org.opengis.util.GenericName; /** * * @author Guilhem Legal (Geomatys) * @author Johann Sorel (Geomatys) * @module pending */ public class OM2FeatureStore extends AbstractFeatureStore { /** the feature factory */ private static final FeatureFactory FF = FeatureFactory.LENIENT; private static final String CSTL_NAMESPACE = "http://constellation.org/om2"; private static final GenericName CSTL_TN_SENSOR = NamesExt.create(CSTL_NAMESPACE, "Sensor"); protected static final GenericName ATT_ID = NamesExt.create(CSTL_NAMESPACE, "id"); protected static final GenericName ATT_POSITION = NamesExt.create(CSTL_NAMESPACE, "position"); private static final QueryCapabilities capabilities = new DefaultQueryCapabilities(false); private final Map<GenericName, FeatureType> types = new HashMap<>(); private final ManageableDataSource source; private final String sensorIdBase = "urn:ogc:object:sensor:GEOM:"; // TODO private final boolean isPostgres; protected final String schemaPrefix; public OM2FeatureStore(final ParameterValueGroup params, final ManageableDataSource source) { super(params); this.source = source; Object sgbdtype = Parameters.value(SGBDTYPE, params); isPostgres = !("derby".equals(sgbdtype)); String sc = Parameters.value(SCHEMA_PREFIX, params); if (sc != null) { schemaPrefix = sc; } else { schemaPrefix = ""; } initTypes(); } @Override public FeatureStoreFactory getFactory() { return FeatureStoreFinder.getFactoryById(OM2FeatureStoreFactory.NAME); } private Connection getConnection() throws SQLException{ return source.getConnection(); } private void initTypes() { final FeatureTypeBuilder featureTypeBuilder = new FeatureTypeBuilder(); featureTypeBuilder.setName(CSTL_TN_SENSOR); featureTypeBuilder.add(ATT_ID, String.class,1,1,false,null); featureTypeBuilder.add(ATT_POSITION,Geometry.class,1,1,false,null); featureTypeBuilder.setDefaultGeometry(ATT_POSITION); types.put(CSTL_TN_SENSOR, featureTypeBuilder.buildFeatureType()); } /** * {@inheritDoc } */ @Override public FeatureReader getFeatureReader(final Query query) throws DataStoreException { final FeatureType sft = getFeatureType(query.getTypeName()); try { return handleRemaining(new OMReader(sft), query); } catch (SQLException ex) { throw new DataStoreException(ex); } } /** * {@inheritDoc } */ @Override public FeatureWriter getFeatureWriterAppend(final GenericName typeName, final Hints hints) throws DataStoreException { return handleWriterAppend(typeName,hints); } /** * {@inheritDoc } */ @Override public FeatureWriter getFeatureWriter(final GenericName typeName, final Filter filter, final Hints hints) throws DataStoreException { final FeatureType sft = getFeatureType(typeName); try { return handleRemaining(new OMWriter(sft), filter); } catch (SQLException ex) { throw new DataStoreException(ex); } } /** * {@inheritDoc } */ @Override public void close() throws DataStoreException { super.close(); try { source.close(); } catch (SQLException ex) { getLogger().info("SQL Exception while closing O&M2 datastore"); } } /** * {@inheritDoc } */ @Override public Set<GenericName> getNames() throws DataStoreException { return types.keySet(); } /** * {@inheritDoc } */ @Override public FeatureType getFeatureType(final GenericName typeName) throws DataStoreException { typeCheck(typeName); return types.get(typeName); } /** * {@inheritDoc } */ @Override public QueryCapabilities getQueryCapabilities() { return capabilities; } /** * {@inheritDoc } */ @Override public List<FeatureId> addFeatures(final GenericName groupName, final Collection<? extends Feature> newFeatures, final Hints hints) throws DataStoreException { final FeatureType featureType = getFeatureType(groupName); //raise an error if type doesn't exist final List<FeatureId> result = new ArrayList<>(); Connection cnx = null; PreparedStatement stmtWrite = null; try { cnx = getConnection(); stmtWrite = cnx.prepareStatement("INSERT INTO \"" + schemaPrefix + "om\".\"procedures\" VALUES(?,?,?)"); for(final Feature feature : newFeatures) { FeatureId identifier = feature.getIdentifier(); if (identifier == null || identifier.getID().isEmpty()) { identifier = getNewFeatureId(); } stmtWrite.setString(1, identifier.getID()); final Object geometry = feature.getDefaultGeometryProperty().getValue(); if (geometry instanceof Geometry) { final Geometry geom = (Geometry) geometry; final WKBWriter writer = new WKBWriter(); final int SRID = geom.getSRID(); stmtWrite.setBytes(2, writer.write(geom)); stmtWrite.setInt(3, SRID); } else { stmtWrite.setNull(2, Types.VARCHAR); stmtWrite.setNull(3, Types.INTEGER); } stmtWrite.executeUpdate(); result.add(identifier); } } catch (SQLException ex) { getLogger().log(Level.WARNING, "Error while writing procedure feature", ex); }finally{ if(stmtWrite != null){ try { stmtWrite.close(); } catch (SQLException ex) { getLogger().log(Level.WARNING, null, ex); } } if(cnx != null){ try { cnx.close(); } catch (SQLException ex) { getLogger().log(Level.WARNING, null, ex); } } } return result; } public FeatureId getNewFeatureId() { Connection cnx = null; PreparedStatement stmtLastId = null; try { cnx = getConnection(); stmtLastId = cnx.prepareStatement("SELECT \"id\" FROM \"" + schemaPrefix + "om\".\"procedures\" ORDER BY \"id\" ASC"); final ResultSet result = stmtLastId.executeQuery(); // keep the last String id = null; while (result.next()) { id = result.getString(1); } if (id != null) { try { final int i = Integer.parseInt(id.substring(sensorIdBase.length())); return new DefaultFeatureId(sensorIdBase + i); } catch (NumberFormatException ex) { getLogger().warning("a snesor ID is malformed in procedures tables"); } } else { return new DefaultFeatureId(sensorIdBase + 1); } } catch (SQLException ex) { getLogger().log(Level.WARNING, null, ex); }finally{ if(stmtLastId != null){ try { stmtLastId.close(); } catch (SQLException ex) { getLogger().log(Level.WARNING, null, ex); } } if(cnx != null){ try { cnx.close(); } catch (SQLException ex) { getLogger().log(Level.WARNING, null, ex); } } } return null; } //////////////////////////////////////////////////////////////////////////// // No supported stuffs ///////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// /** * {@inheritDoc } */ @Override public void createFeatureType(final GenericName typeName, final FeatureType featureType) throws DataStoreException { throw new DataStoreException("Not Supported."); } /** * {@inheritDoc } */ @Override public void updateFeatureType(final GenericName typeName, final FeatureType featureType) throws DataStoreException { throw new DataStoreException("Not Supported."); } /** * {@inheritDoc } */ @Override public void deleteFeatureType(final GenericName typeName) throws DataStoreException { throw new DataStoreException("Not Supported."); } /** * {@inheritDoc } */ @Override public void updateFeatures(final GenericName groupName, final Filter filter, final Map<? extends PropertyDescriptor, ? extends Object> values) throws DataStoreException { throw new DataStoreException("Not supported."); } /** * {@inheritDoc } */ @Override public void removeFeatures(final GenericName groupName, final Filter filter) throws DataStoreException { handleRemoveWithFeatureWriter(groupName, filter); } //////////////////////////////////////////////////////////////////////////// // Feature Reader ////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// private class OMReader implements FeatureReader { protected final Connection cnx; private boolean firstCRS = true; protected final FeatureType type; private final ResultSet result; protected Feature current = null; private OMReader(final FeatureType type) throws SQLException{ this.type = type; cnx = getConnection(); final PreparedStatement stmtAll; if (isPostgres) { stmtAll = cnx.prepareStatement("SELECT \"id\", \"postgis\".st_asBinary(\"shape\"), \"crs\" FROM \"" + schemaPrefix + "om\".\"procedures\""); } else { stmtAll = cnx.prepareStatement("SELECT * FROM \"" + schemaPrefix + "om\".\"procedures\""); } result = stmtAll.executeQuery(); } @Override public FeatureType getFeatureType() { return type; } @Override public Feature next() throws FeatureStoreRuntimeException { try { read(); } catch (Exception ex) { throw new FeatureStoreRuntimeException(ex); } Feature candidate = current; current = null; return candidate; } @Override public boolean hasNext() throws FeatureStoreRuntimeException { try { read(); } catch (Exception ex) { throw new FeatureStoreRuntimeException(ex); } return current != null; } protected void read() throws Exception{ if(current != null) return; if(!result.next()){ return; } final String crsStr = result.getString(3); Geometry geom = null; if (crsStr != null && !crsStr.isEmpty()) { if (firstCRS) { try { CoordinateReferenceSystem crs = CRS.decode("EPSG:" + crsStr); if (type instanceof DefaultSimpleFeatureType) { ((DefaultSimpleFeatureType) type).setCoordinateReferenceSystem(crs); } if (type.getGeometryDescriptor() instanceof DefaultGeometryDescriptor) { ((DefaultGeometryDescriptor) type.getGeometryDescriptor()).setCoordinateReferenceSystem(crs); } firstCRS = false; } catch (NoSuchAuthorityCodeException ex) { throw new IOException(ex); } catch (FactoryException ex) { throw new IOException(ex); } } final byte[] b = result.getBytes(2); if (b != null) { WKBReader reader = new WKBReader(); geom = reader.read(b); } } final Collection<Property> props = new ArrayList<>(); final String id = result.getString(1); props.add(FF.createAttribute(id, (AttributeDescriptor) type.getDescriptor(ATT_ID), null)); props.add(FF.createAttribute(geom, (AttributeDescriptor) type.getDescriptor(ATT_POSITION), null)); //props.add(FF.createAttribute(result.getString("description"), (AttributeDescriptor) type.getDescriptor(ATT_DESC), null)); current = FF.createFeature(props, type, id); } @Override public void close() { try { result.close(); cnx.close(); } catch (SQLException ex) { throw new FeatureStoreRuntimeException(ex); } } @Override public void remove() throws FeatureStoreRuntimeException{ throw new FeatureStoreRuntimeException("Not supported."); } } private class OMWriter extends OMReader implements FeatureWriter { protected Feature candidate = null; private OMWriter(final FeatureType type) throws SQLException{ super(type); } @Override public Feature next() throws FeatureStoreRuntimeException { try { read(); } catch (Exception ex) { throw new FeatureStoreRuntimeException(ex); } candidate = current; current = null; return candidate; } @Override public void remove() throws FeatureStoreRuntimeException{ if (candidate == null) { return; } PreparedStatement stmtDelete = null; try { stmtDelete = cnx.prepareStatement("DELETE FROM \"" + schemaPrefix + "om\".\"procedures\" WHERE \"id\" = ?"); stmtDelete.setString(1, candidate.getIdentifier().getID()); stmtDelete.executeUpdate(); } catch (SQLException ex) { getLogger().log(Level.WARNING, "Error while deleting procedure features", ex); } finally { if (stmtDelete != null) { try { stmtDelete.close(); } catch (SQLException ex) { getLogger().log(Level.WARNING, null, ex); } } } } @Override public void write() throws FeatureStoreRuntimeException { throw new FeatureStoreRuntimeException("Not supported."); } } @Override public void refreshMetaModel() { } }