/*
* 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.sos.io.generic;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.util.logging.Logging;
import org.constellation.generic.database.Automatic;
import org.constellation.generic.database.BDD;
import org.constellation.generic.database.Query;
import org.constellation.sos.factory.OMFactory;
import org.constellation.ws.CstlServiceException;
import org.geotoolkit.observation.ObservationFilter;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.geotoolkit.ows.xml.OWSExceptionCode.NO_APPLICABLE_CODE;
/**
*
* @author Guilhem Legal
*/
public abstract class AbstractGenericObservationFilter implements ObservationFilter {
/**
* The base whole configuration query extract from the file Affinage.xml
*/
protected final Query configurationQuery;
/**
* A map of static variable to replace in the statements.
*/
protected HashMap<String, Object> staticParameters = new HashMap<>();
/**
* The current query built by the sos worker in the scope of a getObservation/getResult request.
*/
protected Query currentQuery;
/**
* The base for observation id.
*/
protected final String observationIdBase;
/**
* The base for observation id.
*/
protected final String observationTemplateIdBase;
protected final String phenomenonIdBase;
/**
* The O&M data source
*/
protected DataSource dataSource;
/**
* A flag indicating that the service is trying to reconnect the database.
*/
private boolean isReconnecting = false;
/**
* The database informations.
*/
private Automatic configuration;
protected Level logLevel = Level.INFO;
/**
* use for debugging purpose
*/
protected static final Logger LOGGER = Logging.getLogger("org.constellation.sos.io.generic");
public AbstractGenericObservationFilter(final Automatic configuration, final Map<String, Object> properties) throws DataStoreException {
this.observationIdBase = (String) properties.get(OMFactory.OBSERVATION_ID_BASE);
this.observationTemplateIdBase = (String) properties.get(OMFactory.OBSERVATION_TEMPLATE_ID_BASE);
this.phenomenonIdBase = (String) properties.get(OMFactory.PHENOMENON_ID_BASE);
if (configuration == null) {
throw new DataStoreException("The configuration object is null");
}
this.configuration = configuration;
// we get the database informations
final BDD db = configuration.getBdd();
if (db == null) {
throw new DataStoreException("The configuration file does not contains a BDD object");
}
this.configurationQuery = configuration.getFilterQueries();
if (configurationQuery == null) {
throw new DataStoreException("Unable to find the filter queries part");
}
try {
this.dataSource = db.getDataSource();
if (configurationQuery.getStatique() != null) {
for (Query query : configurationQuery.getStatique().getQuery()) {
processStatiqueQuery(query);
}
}
} catch (SQLException ex) {
throw new DataStoreException("SQLException while initializing the observation filter:\ncause:" + ex.getMessage());
}
}
public AbstractGenericObservationFilter(final AbstractGenericObservationFilter that) {
this.observationIdBase = that.observationIdBase;
this.observationTemplateIdBase = that.observationTemplateIdBase;
this.configurationQuery = that.configurationQuery;
this.dataSource = that.dataSource;
this.logLevel = that.logLevel;
this.staticParameters = that.staticParameters;
this.phenomenonIdBase = that.phenomenonIdBase;
}
private void processStatiqueQuery(final Query query) throws SQLException {
final Connection connection = acquireConnection();
final Statement stmt = connection.createStatement();
final List<String> varNames = query.getVarNames();
final String textQuery = query.buildSQLQuery(staticParameters);
try {
final ResultSet res = stmt.executeQuery(textQuery);
final Map<String, StringBuilder> parameterValue = new HashMap<>();
for (String varName : varNames) {
parameterValue.put(varName, new StringBuilder());
}
while (res.next()) {
for (String varName : varNames) {
final StringBuilder builder = parameterValue.get(varName);
builder.append("'").append(res.getString(varName)).append("',");
}
}
res.close();
//we remove the last ','
for (String varName : varNames) {
final StringBuilder builder = parameterValue.get(varName);
final String pValue;
if (builder.length() > 0) {
pValue = builder.substring(0, builder.length() - 1);
} else {
pValue = "";
}
staticParameters.put(varName, pValue);
}
} catch (SQLException ex) {
LOGGER.log(Level.WARNING, "SQL exception while executing static query :{0}", textQuery);
throw ex;
}
}
/**
* {@inheritDoc}
*/
@Override
public void setLoglevel(final Level logLevel) {
this.logLevel = logLevel;
}
/**
* {@inheritDoc}
*/
@Override
public void refresh() {
// do nothing
}
/**
* {@inheritDoc}
*/
@Override
public List<String> supportedQueryableResultProperties() {
return new ArrayList<>();
}
/**
* {@inheritDoc}
*/
@Override
public void setResultEquals(final String propertyName, final String value) throws DataStoreException {
throw new DataStoreException("setResultEquals is not supported by this ObservationFilter implementation.");
}
/**
* Try to reconnect to the database if the connection have been lost.
*
* @throws org.constellation.ws.CstlServiceException
*/
protected void reloadConnection() throws CstlServiceException {
if (!isReconnecting) {
try {
LOGGER.info("refreshing the connection");
BDD db = configuration.getBdd();
this.dataSource = db.getDataSource();
isReconnecting = false;
} catch(SQLException ex) {
LOGGER.log(Level.SEVERE, "SQLException while restarting the connection:{0}", ex);
isReconnecting = false;
}
}
throw new CstlServiceException("The database connection has been lost, the service is trying to reconnect", NO_APPLICABLE_CODE);
}
protected Connection acquireConnection() throws SQLException {
final Connection c = dataSource.getConnection();
c.setReadOnly(true);
c.setAutoCommit(false);
return c;
}
@Override
public boolean isDefaultTemplateTime() {
return true;
}
}