/*
* 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.mdweb;
import org.apache.sis.internal.jaxb.LegacyNamespaces;
import org.constellation.generic.database.Automatic;
import org.constellation.generic.database.BDD;
import org.constellation.metadata.io.MDWebMetadataWriter;
import org.constellation.metadata.io.MetadataIoException;
import org.constellation.sos.factory.OMFactory;
import org.constellation.sos.io.SensorWriter;
import org.constellation.ws.CstlServiceException;
import org.geotoolkit.sml.xml.AbstractSensorML;
import org.geotoolkit.sos.xml.SOSMarshallerPool;
import org.mdweb.io.MD_IOException;
import org.mdweb.io.sql.AbstractReader;
import org.mdweb.model.storage.RecordSet;
import org.mdweb.model.storage.RecordSet.EXPOSURE;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import javax.sql.DataSource;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Savepoint;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.logging.Level;
import static org.geotoolkit.ows.xml.OWSExceptionCode.*;
// JAXB dependencies
// Constellation dependencies
// MDWeb dependencies
/**
*
* @author Guilhem Legal (geomatys)
*/
public class MDWebSensorWriter extends MDWebMetadataWriter implements SensorWriter {
private static final String SQL_ERROR_MSG = "The service has throw a SQL Exception:";
/**
* A connection to the MDWeb database.
*/
private final Connection smlConnection;
private Savepoint currentSavePoint;
/**
* An SQL statement finding the last sensor ID recorded
*/
private final PreparedStatement newSensorIdStmt;
public MDWebSensorWriter(final Automatic configuration, final Map<String, Object> properties) throws MetadataIoException {
super(configuration);
final String sensorIdBase = (String) properties.get(OMFactory.SENSOR_ID_BASE);
final BDD db = configuration.getBdd();
try {
final DataSource ds = db.getPooledDataSource();
smlConnection = ds.getConnection();
//we build the prepared Statement
final String version = ((AbstractReader)mdWriter).getVersion();
if ("2.0".equals(version)) {
newSensorIdStmt = smlConnection.prepareStatement("SELECT Count(*) FROM \"Storage\".\"Forms\" WHERE \"title\" LIKE '%" + sensorIdBase + "%' ");
} else if (version.startsWith("2.1") || version.startsWith("2.2") || version.startsWith("2.3") || version.startsWith("2.4")) {
newSensorIdStmt = smlConnection.prepareStatement("SELECT Count(*) FROM \"Storage\".\"Records\" WHERE \"title\" LIKE '%" + sensorIdBase + "%' ");
} else {
throw new IllegalArgumentException("unexpected MDWeb database version:" + version);
}
// we enbale the fast storage mode
mdWriter.setProperty("fastStorage", true);
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
throw new MetadataIoException("SQLException while starting the MDweb Sensor writer: " + "\n" + ex.getMessage(), NO_APPLICABLE_CODE);
}
}
/**
* {@inheritDoc}
*/
@Override
public RecordSet getRecordSet(final String recordSet) throws MD_IOException {
RecordSet cat = mdWriter.getRecordSet("SMLC");
if (cat == null) {
cat = new RecordSet("SMLC", "SensorML RecordSet", null, null, EXPOSURE.EXTERNAL, 0, new Date(System.currentTimeMillis()), true);
mdWriter.writeRecordSet(cat);
}
return cat;
}
/**
* {@inheritDoc}
*/
@Override
public boolean writeSensor(final String id, final AbstractSensorML process) throws CstlServiceException {
try {
final Node n = marshallProcess(process);
return super.storeMetadata(n, id);
} catch (MetadataIoException e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
throw new CstlServiceException(SQL_ERROR_MSG + e.getMessage(),
NO_APPLICABLE_CODE);
}
}
private Node marshallProcess(final AbstractSensorML process) throws MetadataIoException {
try {
final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
final DocumentBuilder docBuilder = dbf.newDocumentBuilder();
final Document document = docBuilder.newDocument();
Marshaller marshaller = SOSMarshallerPool.getInstance().acquireMarshaller();
marshaller.setProperty(LegacyNamespaces.APPLY_NAMESPACE_REPLACEMENTS, false);
marshaller.marshal(process, document);
SOSMarshallerPool.getInstance().recycle(marshaller);
return document.getDocumentElement();
} catch (ParserConfigurationException | JAXBException ex) {
throw new MetadataIoException(ex);
}
}
/**
* {@inheritDoc}
*/
@Override
public int replaceSensor(final String sensorid, final AbstractSensorML process) throws CstlServiceException {
final boolean deleted = deleteSensor(sensorid);
int result;
if (deleted) {
result = 1;//REPLACED;
} else {
result = 0; //INSERTED;
}
writeSensor(sensorid, process);
return result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean deleteSensor(final String sensorId) throws CstlServiceException {
try {
return super.deleteMetadata(sensorId);
} catch (MetadataIoException ex) {
LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
throw new CstlServiceException("the service has throw a Metadata IO Exception:" + ex.getMessage(),
NO_APPLICABLE_CODE);
}
}
/**
* This method shouldn't be called. It is used in a subProject in order to clear the database.
*
* @return
* @throws CstlServiceException
*/
public void deleteAllSensor() throws CstlServiceException {
try {
final RecordSet smlCat = mdWriter.getRecordSet("SMLC");
final Collection<String> allIdentifier = mdWriter.getAllIdentifiers(Arrays.asList(smlCat), false);
for (String identifier : allIdentifier) {
LOGGER.log(Level.FINER, "sensor id: {0}", identifier);
super.deleteMetadata(identifier);
}
} catch (MD_IOException ex) {
LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
throw new CstlServiceException("the service has throw a MD_IO Exception:" + ex.getMessage(),
NO_APPLICABLE_CODE);
} catch (MetadataIoException ex) {
LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
throw new CstlServiceException("the service has throw a Metadata IO Exception:" + ex.getMessage(),
NO_APPLICABLE_CODE);
}
}
/**
* {@inheritDoc}
*/
@Override
public void startTransaction() throws CstlServiceException {
try {
smlConnection.setAutoCommit(false);
currentSavePoint = smlConnection.setSavepoint("registerSensorTransaction");
} catch (SQLException e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
throw new CstlServiceException(SQL_ERROR_MSG + e.getMessage(),
NO_APPLICABLE_CODE);
}
}
/**
* {@inheritDoc}
*/
@Override
public void abortTransaction() throws CstlServiceException {
try {
if (currentSavePoint != null) {
smlConnection.rollback(currentSavePoint);
}
smlConnection.commit();
smlConnection.setAutoCommit(true);
} catch (SQLException e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
throw new CstlServiceException(SQL_ERROR_MSG + e.getMessage(),
NO_APPLICABLE_CODE);
}
}
/**
* {@inheritDoc}
*/
@Override
public void endTransaction() throws CstlServiceException {
try {
if (currentSavePoint != null) {
smlConnection.releaseSavepoint(currentSavePoint);
}
smlConnection.commit();
smlConnection.setAutoCommit(true);
} catch (SQLException e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
throw new CstlServiceException(SQL_ERROR_MSG + e.getMessage(),
NO_APPLICABLE_CODE);
}
}
/**
* {@inheritDoc}
*/
@Override
public int getNewSensorId() throws CstlServiceException {
try {
final ResultSet res = newSensorIdStmt.executeQuery();
int id = -1;
while (res.next()) {
id = res.getInt(1);
}
res.close();
return id + 1;
} catch (SQLException e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
throw new CstlServiceException(SQL_ERROR_MSG + e.getMessage(),
NO_APPLICABLE_CODE);
}
}
/**
* {@inheritDoc}
*/
@Override
public void destroy() {
super.destroy();
try {
newSensorIdStmt.close();
smlConnection.close();
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "SQLException while closing MDW sensor Writer:{0}", ex.getMessage());
}
}
/**
* {@inheritDoc}
*/
@Override
public String getInfos() {
return "Constellation MDweb Sensor Writer 0.9";
}
}