/**
* Copyright (C) 2012-2017 52°North Initiative for Geospatial Open Source
* Software GmbH
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* If the program is linked with libraries which are licensed under one of
* the following licenses, the combination of the program with the linked
* library is not considered a "derivative work" of the program:
*
* - Apache License, version 2.0
* - Apache Software License, version 1.0
* - GNU Lesser General Public License, version 3
* - Mozilla Public License, versions 1.0, 1.1 and 2.0
* - Common Development and Distribution License (CDDL), version 1.0
*
* Therefore the distribution of the program linked with libraries licensed
* under the aforementioned licenses, is permitted by the copyright holders
* if the distribution is compliant with both the GNU General Public
* License version 2 and the aforementioned licenses.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*/
package org.n52.sos.ds.hibernate.dao;
import static org.n52.sos.util.http.HTTPStatus.BAD_REQUEST;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.Subqueries;
import org.hibernate.sql.JoinType;
import org.n52.sos.ds.hibernate.dao.series.SeriesObservationDAO;
import org.n52.sos.ds.hibernate.entities.AbstractObservation;
import org.n52.sos.ds.hibernate.entities.EntitiyHelper;
import org.n52.sos.ds.hibernate.entities.FeatureOfInterest;
import org.n52.sos.ds.hibernate.entities.FeatureOfInterestType;
import org.n52.sos.ds.hibernate.entities.ObservationConstellation;
import org.n52.sos.ds.hibernate.entities.ObservationInfo;
import org.n52.sos.ds.hibernate.entities.Offering;
import org.n52.sos.ds.hibernate.entities.RelatedFeature;
import org.n52.sos.ds.hibernate.entities.TFeatureOfInterest;
import org.n52.sos.ds.hibernate.entities.series.Series;
import org.n52.sos.ds.hibernate.entities.series.SeriesObservationInfo;
import org.n52.sos.ds.hibernate.util.HibernateHelper;
import org.n52.sos.ds.hibernate.util.NoopTransformerAdapter;
import org.n52.sos.exception.CodedException;
import org.n52.sos.exception.ows.NoApplicableCodeException;
import org.n52.sos.ogc.OGCConstants;
import org.n52.sos.ogc.gml.AbstractFeature;
import org.n52.sos.ogc.om.features.samplingFeatures.SamplingFeature;
import org.n52.sos.ogc.ows.OwsExceptionReport;
import org.n52.sos.service.Configurator;
import org.n52.sos.util.CollectionHelper;
import org.n52.sos.util.http.HTTPStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Maps;
/**
* Hibernate data access class for featureOfInterest
*
* @author CarstenHollmann
* @since 4.0.0
*/
public class FeatureOfInterestDAO extends AbstractIdentifierNameDescriptionDAO implements HibernateSqlQueryConstants {
private static final Logger LOGGER = LoggerFactory.getLogger(FeatureOfInterestDAO.class);
private static final String SQL_QUERY_GET_FEATURE_OF_INTEREST_IDENTIFIER_FOR_OFFERING =
"getFeatureOfInterestIdentifiersForOffering";
private static final String SQL_QUERY_GET_FEATURE_OF_INTEREST_IDENTIFIER_FOR_OBSERVATION_CONSTELLATION =
"getFeatureOfInterestIdentifiersForObservationConstellation";
/**
* Get featureOfInterest object for identifier
*
* @param identifier
* FeatureOfInterest identifier
* @param session
* Hibernate session Hibernate session
* @return FeatureOfInterest entity
*/
public FeatureOfInterest getFeatureOfInterest(final String identifier, final Session session) {
Criteria criteria =
session.createCriteria(FeatureOfInterest.class).add(
Restrictions.eq(FeatureOfInterest.IDENTIFIER, identifier));
LOGGER.debug("QUERY getFeatureOfInterest(identifier): {}", HibernateHelper.getSqlString(criteria));
return (FeatureOfInterest) criteria.uniqueResult();
}
/**
* Get featureOfInterest identifiers for observation constellation
*
* @param observationConstellation
* Observation constellation
* @param session
* Hibernate session Hibernate session
* @return FeatureOfInterest identifiers for observation constellation
* @throws CodedException
*/
@SuppressWarnings("unchecked")
public List<String> getFeatureOfInterestIdentifiersForObservationConstellation(
final ObservationConstellation observationConstellation, final Session session) throws OwsExceptionReport {
if (HibernateHelper.isNamedQuerySupported(
SQL_QUERY_GET_FEATURE_OF_INTEREST_IDENTIFIER_FOR_OBSERVATION_CONSTELLATION, session)) {
Query namedQuery =
session.getNamedQuery(SQL_QUERY_GET_FEATURE_OF_INTEREST_IDENTIFIER_FOR_OBSERVATION_CONSTELLATION);
namedQuery.setParameter(PROCEDURE, observationConstellation.getProcedure().getIdentifier());
namedQuery.setParameter(OBSERVABLE_PROPERTY, observationConstellation.getObservableProperty()
.getIdentifier());
namedQuery.setParameter(OFFERING, observationConstellation.getOffering().getIdentifier());
LOGGER.debug(
"QUERY getFeatureOfInterestIdentifiersForObservationConstellation(observationConstellation) with NamedQuery: {}",
SQL_QUERY_GET_FEATURE_OF_INTEREST_IDENTIFIER_FOR_OBSERVATION_CONSTELLATION);
return namedQuery.list();
} else {
AbstractObservationDAO observationDAO = DaoFactory.getInstance().getObservationDAO();
Criteria criteria = observationDAO.getDefaultObservationInfoCriteria(session);
if (observationDAO instanceof SeriesObservationDAO) {
Criteria seriesCriteria = criteria.createCriteria(SeriesObservationInfo.SERIES);
seriesCriteria.add(Restrictions.eq(Series.PROCEDURE, observationConstellation.getProcedure())).add(
Restrictions.eq(Series.OBSERVABLE_PROPERTY, observationConstellation.getObservableProperty()));
seriesCriteria.createCriteria(Series.FEATURE_OF_INTEREST).setProjection(
Projections.distinct(Projections.property(FeatureOfInterest.IDENTIFIER)));
} else {
criteria.add(Restrictions.eq(ObservationConstellation.PROCEDURE, observationConstellation.getProcedure()))
.add(Restrictions.eq(ObservationConstellation.OBSERVABLE_PROPERTY,
observationConstellation.getObservableProperty()));
criteria.createCriteria(ObservationInfo.FEATURE_OF_INTEREST).setProjection(
Projections.distinct(Projections.property(FeatureOfInterest.IDENTIFIER)));
}
criteria.createCriteria(AbstractObservation.OFFERINGS).add(
Restrictions.eq(Offering.ID, observationConstellation.getOffering().getOfferingId()));
LOGGER.debug(
"QUERY getFeatureOfInterestIdentifiersForObservationConstellation(observationConstellation): {}",
HibernateHelper.getSqlString(criteria));
return criteria.list();
}
}
/**
* Get featureOfInterest identifiers for an offering identifier
*
* @param offeringIdentifiers
* Offering identifier
* @param session
* Hibernate session Hibernate session
* @return FeatureOfInterest identifiers for offering
* @throws CodedException
*/
@SuppressWarnings({ "unchecked" })
public List<String> getFeatureOfInterestIdentifiersForOffering(final String offeringIdentifiers,
final Session session) throws OwsExceptionReport {
if (HibernateHelper.isNamedQuerySupported(SQL_QUERY_GET_FEATURE_OF_INTEREST_IDENTIFIER_FOR_OFFERING, session)) {
Query namedQuery = session.getNamedQuery(SQL_QUERY_GET_FEATURE_OF_INTEREST_IDENTIFIER_FOR_OFFERING);
namedQuery.setParameter(OFFERING, offeringIdentifiers);
LOGGER.debug("QUERY getFeatureOfInterestIdentifiersForOffering(offeringIdentifiers) with NamedQuery: {}",
SQL_QUERY_GET_FEATURE_OF_INTEREST_IDENTIFIER_FOR_OFFERING);
return namedQuery.list();
} else {
Criteria c = null;
if (EntitiyHelper.getInstance().isSeriesSupported()) {
c = session.createCriteria(FeatureOfInterest.class)
.setProjection(Projections.distinct(Projections.property(FeatureOfInterest.IDENTIFIER)));
c.add(Subqueries.propertyIn(FeatureOfInterest.ID,
getDetachedCriteriaSeriesForOffering(offeringIdentifiers, session)));
List list = c.list();
if (list == null || (list != null && list.isEmpty())) {
c = DaoFactory.getInstance().getObservationDAO().getDefaultObservationInfoCriteria(session);
Criteria seriesCriteria = c.createCriteria(SeriesObservationInfo.SERIES);
seriesCriteria.createCriteria(Series.FEATURE_OF_INTEREST)
.setProjection(Projections.distinct(Projections.property(FeatureOfInterest.IDENTIFIER)));
new OfferingDAO().addOfferingRestricionForObservation(c, offeringIdentifiers);
LOGGER.debug("QUERY getFeatureOfInterestIdentifiersForOffering(offeringIdentifiers): {}",
HibernateHelper.getSqlString(c));
}
LOGGER.debug("QUERY getFeatureOfInterestIdentifiersForOffering(offeringIdentifiers): {}",
HibernateHelper.getSqlString(c));
return list;
} else {
c = DaoFactory.getInstance().getObservationDAO().getDefaultObservationInfoCriteria(session);
c.createCriteria(AbstractObservation.FEATURE_OF_INTEREST)
.setProjection(Projections.distinct(Projections.property(FeatureOfInterest.IDENTIFIER)));
new OfferingDAO().addOfferingRestricionForObservation(c, offeringIdentifiers);
}
LOGGER.debug("QUERY getFeatureOfInterestIdentifiersForOffering(offeringIdentifiers): {}",
HibernateHelper.getSqlString(c));
return c.list();
}
}
private DetachedCriteria getDetachedCriteriaSeriesForOffering(String offering, Session session) throws CodedException {
final DetachedCriteria detachedCriteria = getDetachedCriteriaSeries(session);
detachedCriteria.createCriteria(Series.OFFERING).add(Restrictions.eq(Offering.IDENTIFIER, offering));
return detachedCriteria;
}
/**
* Get featureOfInterest objects for featureOfInterest identifiers
*
* @param identifiers
* FeatureOfInterest identifiers
* @param session
* Hibernate session
* @return FeatureOfInterest objects
*/
@SuppressWarnings("unchecked")
public List<FeatureOfInterest> getFeatureOfInterestObject(final Collection<String> identifiers,
final Session session) {
if (identifiers != null && !identifiers.isEmpty()) {
Criteria criteria =
session.createCriteria(FeatureOfInterest.class).add(
Restrictions.in(FeatureOfInterest.IDENTIFIER, identifiers));
LOGGER.debug("QUERY getFeatureOfInterestObject(identifiers): {}", HibernateHelper.getSqlString(criteria));
return criteria.list();
}
return Collections.emptyList();
}
/**
* Get all featureOfInterest objects
*
* @param session
* Hibernate session
* @return FeatureOfInterest objects
*/
@SuppressWarnings("unchecked")
public List<FeatureOfInterest> getFeatureOfInterestObjects(final Session session) {
Criteria criteria = getDefaultCriteria(session);
LOGGER.debug("QUERY getFeatureOfInterestObjects(identifier): {}", HibernateHelper.getSqlString(criteria));
return criteria.list();
}
private Criteria getDefaultCriteria(final Session session) {
return session.createCriteria(FeatureOfInterest.class);
}
/**
* Load FOI identifiers and parent ids for use in the cache. Just loading the ids allows us to not load
* the geometry columns, XML, etc.
*
* @param session
* @return Map keyed by FOI identifiers, with value collections of parent FOI identifiers if supported
*/
public Map<String,Collection<String>> getFeatureOfInterestIdentifiersWithParents(final Session session) {
Criteria criteria = session.createCriteria(FeatureOfInterest.class);
ProjectionList projectionList = Projections.projectionList();
projectionList.add(Projections.property(FeatureOfInterest.IDENTIFIER));
//get parents if transactional profile is active
boolean tFoiSupported = HibernateHelper.isEntitySupported(TFeatureOfInterest.class);
if (tFoiSupported) {
criteria.createAlias(TFeatureOfInterest.PARENTS, "pfoi", JoinType.LEFT_OUTER_JOIN);
projectionList.add(Projections.property("pfoi." + FeatureOfInterest.IDENTIFIER));
}
criteria.setProjection(projectionList);
//return as List<Object[]> even if there's only one column for consistency
criteria.setResultTransformer(NoopTransformerAdapter.INSTANCE);
LOGGER.debug("QUERY getFeatureOfInterestIdentifiersWithParents(): {}", HibernateHelper.getSqlString(criteria));
@SuppressWarnings("unchecked")
List<Object[]> results = criteria.list();
Map<String,Collection<String>> foiMap = Maps.newHashMap();
for(Object[] result : results) {
String featureIdentifier = (String) result[0];
String parentFeatureIdentifier = null;
if (tFoiSupported) {
parentFeatureIdentifier = (String) result[1];
}
if (parentFeatureIdentifier != null) {
CollectionHelper.addToCollectionMap(featureIdentifier, parentFeatureIdentifier, foiMap);
} else {
foiMap.put(featureIdentifier, null);
}
}
return foiMap;
}
/**
* Get all featureOfInterest identifiers
*
* @param session
* Hibernate session
* @return FeatureOfInterest identifiers
*/
@SuppressWarnings("unchecked")
public List<String> getFeatureOfInterestIdentifiers(Session session) {
Criteria criteria =
session.createCriteria(FeatureOfInterest.class).setProjection(
Projections.distinct(Projections.property(FeatureOfInterest.IDENTIFIER)));
LOGGER.debug("QUERY getFeatureOfInterestIdentifiers(): {}", HibernateHelper.getSqlString(criteria));
return criteria.list();
}
/**
* Insert and/or get featureOfInterest object for identifier
*
* @param identifier
* FeatureOfInterest identifier
* @param url
* FeatureOfInterest URL, if defined as link
* @param session
* Hibernate session
* @return FeatureOfInterest object
*/
public FeatureOfInterest getOrInsertFeatureOfInterest(final String identifier, final String url,
final Session session) {
FeatureOfInterest feature = getFeatureOfInterest(identifier, session);
if (feature == null) {
feature = new TFeatureOfInterest();
feature.setIdentifier(identifier);
if (url != null && !url.isEmpty()) {
feature.setUrl(url);
}
final FeatureOfInterestType featureOfInterestType =
new FeatureOfInterestTypeDAO().getOrInsertFeatureOfInterestType(OGCConstants.UNKNOWN, session);
feature.setFeatureOfInterestType(featureOfInterestType);
session.save(feature);
} else if (feature.getUrl() != null && !feature.getUrl().isEmpty() && url != null && !url.isEmpty()) {
feature.setUrl(url);
session.saveOrUpdate(feature);
}
//don't flush here because we may be batching
return feature;
}
/**
* Insert featureOfInterest relationship
*
* @param parentFeature
* Parent featureOfInterest
* @param childFeature
* Child featureOfInterest
* @param session
* Hibernate session
*/
public void insertFeatureOfInterestRelationShip(final TFeatureOfInterest parentFeature,
final FeatureOfInterest childFeature, final Session session) {
parentFeature.getChilds().add(childFeature);
session.saveOrUpdate(parentFeature);
//don't flush here because we may be batching
}
/**
* Insert featureOfInterest/related feature relations if relatedFeatures
* exists for offering.
*
* @param featureOfInterest
* FeatureOfInerest
* @param offering
* Offering
* @param session
* Hibernate session
*/
public void checkOrInsertFeatureOfInterestRelatedFeatureRelation(final FeatureOfInterest featureOfInterest,
final Offering offering, final Session session) {
final List<RelatedFeature> relatedFeatures =
new RelatedFeatureDAO().getRelatedFeatureForOffering(offering.getIdentifier(), session);
if (CollectionHelper.isNotEmpty(relatedFeatures)) {
for (final RelatedFeature relatedFeature : relatedFeatures) {
if (!featureOfInterest.getIdentifier().equals(relatedFeature.getFeatureOfInterest().getIdentifier())) {
insertFeatureOfInterestRelationShip((TFeatureOfInterest) relatedFeature.getFeatureOfInterest(),
featureOfInterest, session);
}
}
}
}
/**
* Insert featureOfInterest if it is supported
*
* @param featureOfInterest
* SOS featureOfInterest to insert
* @param session
* Hibernate session
* @return FeatureOfInterest object
* @throws NoApplicableCodeException
* If SOS feature type is not supported (with status
* {@link HTTPStatus}.BAD_REQUEST
*/
public FeatureOfInterest checkOrInsertFeatureOfInterest(final AbstractFeature featureOfInterest,
final Session session) throws OwsExceptionReport {
if (featureOfInterest instanceof SamplingFeature) {
final String featureIdentifier =
Configurator.getInstance().getFeatureQueryHandler()
.insertFeature((SamplingFeature) featureOfInterest, session);
return getOrInsertFeatureOfInterest(featureIdentifier, ((SamplingFeature) featureOfInterest).getUrl(),
session);
} else {
throw new NoApplicableCodeException().withMessage("The used feature type '%s' is not supported.",
featureOfInterest != null ? featureOfInterest.getClass().getName() : featureOfInterest).setStatus(
BAD_REQUEST);
}
}
@SuppressWarnings("unchecked")
public List<FeatureOfInterest> getPublishedFeatureOfInterest(Session session) throws CodedException {
Criteria c = getPublishedFeatureOfInterestCriteria(session);
LOGGER.debug("QUERY getPublishedFeatureOfInterest(): {}", HibernateHelper.getSqlString(c));
return c.list();
}
public Criteria getPublishedFeatureOfInterestCriteria(Session session) throws CodedException {
Criteria c = getDefaultCriteria(session);
if (HibernateHelper.isEntitySupported(Series.class)) {
c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
c.add(Subqueries.propertyIn(FeatureOfInterest.ID, getDetachedCriteriaSeries(session)));
}
return c;
}
private DetachedCriteria getDetachedCriteriaSeries(Session session) throws CodedException {
final DetachedCriteria detachedCriteria =
DetachedCriteria.forClass(DaoFactory.getInstance().getSeriesDAO().getSeriesClass());
detachedCriteria.add(Restrictions.eq(Series.DELETED, false)).add(Restrictions.eq(Series.PUBLISHED, true));
detachedCriteria.setProjection(Projections.distinct(Projections.property(Series.FEATURE_OF_INTEREST)));
return detachedCriteria;
}
@SuppressWarnings("unchecked")
public List<String> getPublishedFeatureOfInterestIdentifiers(Session session) throws CodedException {
Criteria c = getPublishedFeatureOfInterestCriteria(session);
c.setProjection(Projections.distinct(Projections.property(FeatureOfInterest.IDENTIFIER)));
LOGGER.debug("QUERY getPublishedFeatureOfInterestIdentifiers(): {}", HibernateHelper.getSqlString(c));
return c.list();
}
}