/*
* Copyright 2013, Cristiano Costantini, Giuseppe Gerla, Michele Ficarra, Sergio Ciampi, Stefano
* Cigheri.
*
* 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 com.google.developers.gdgfirenze.dataservice;
import com.google.developers.gdgfirenze.datamodeljpa.JpaAbstractSample;
import com.google.developers.gdgfirenze.datamodeljpa.JpaSensor;
import com.google.developers.gdgfirenze.model.AbstractSample;
import com.google.developers.gdgfirenze.model.DailySampleReport;
import com.google.developers.gdgfirenze.model.SampleReport;
import com.google.developers.gdgfirenze.model.Sensor;
import com.google.developers.gdgfirenze.osgi.SensormixAdminInterface;
import com.google.developers.gdgfirenze.serializer.Serializer;
import com.google.developers.gdgfirenze.service.SensormixService;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
/**
* The Class SensormixServiceJpaImpl. This class implements the methods from the SensormixService
* interface using JPA to persist on a database main data structures from the data model project:
* samples and sensors. Because this class use JPA it is possible to configure it to store data in
* any of the major existing DBMS.
* This class implements also SensormixAdminInterface to manage maintenance operations.
*/
public class SensormixServiceJpaImpl implements SensormixService, SensormixAdminInterface {
/**
* The class logger.
*/
private static Logger logger = Logger.getLogger(SensormixServiceJpaImpl.class.getName());
/**
* Serializer that wrap Kryo instance to be thread safe.
*/
private static final ThreadLocal<Serializer> localSerializer = new ThreadLocal<Serializer>() {
@Override
protected Serializer initialValue() {
logger.log(Level.INFO, "Initializing a new Kryo instance");
final Serializer serializer = new Serializer();
return serializer;
}
};
/**
* The Entity Manager factory to use JPA implementation.
*/
private EntityManagerFactory entityManagerFactory;
/**
* Sets the entity manager factory (to use with injection).
*
* @param entityManagerFactory the new entity manager factory
*/
public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
}
/**
* To manage maintenance state.
*/
private boolean inMaintenance;
/*
* (non-Javadoc)
*
* @see
* com.google.developers.gdgfirenze.service.SensormixService#listSensorsIds()
*/
@Override
public List<String> listSensorsIds() {
List<String> result = new ArrayList<String>();
try {
EntityManager em = entityManagerFactory.createEntityManager();
TypedQuery<String> q = em.createQuery("SELECT s.id FROM JpaSensor s", String.class);
result.addAll(q.getResultList());
em.close();
} catch (Exception e) {
logger.log(Level.SEVERE, "Error during sensors list retrieving", e);
}
return result;
}
/*
* (non-Javadoc)
*
* @see
* com.google.developers.gdgfirenze.service.SensormixService#getSamples(java
* .lang.String, java.lang.String, java.util.Date, java.util.Date,
* java.lang.Long, java.lang.Long)
*/
@Override
public List<AbstractSample> getSamples(String sensorId, String sampleType, Date from, Date to,
Long limitFrom, Long limitCount) {
List<AbstractSample> samples = new ArrayList<>();
try {
if (from != null && to != null && !from.before(to)) {
logger.log(Level.WARNING, "Error from date must be before to date");
} else {
EntityManager em = entityManagerFactory.createEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<JpaAbstractSample> cq = cb.createQuery(JpaAbstractSample.class);
Root<JpaAbstractSample> jas = cq.from(JpaAbstractSample.class);
cq.select(jas);
cq.orderBy(cb.desc(jas.get("time")));
List<Predicate> criteria = new ArrayList<Predicate>();
if (sensorId != null && !"".equals(sensorId)) {
ParameterExpression<String> p = cb.parameter(String.class, "sensorId");
criteria.add(cb.equal(jas.get("sensorId"), p));
}
if (sampleType != null && !"".equals(sampleType)) {
ParameterExpression<String> p = cb.parameter(String.class, "sampleType");
criteria.add(cb.equal(jas.get("type"), p));
}
if (from != null) {
ParameterExpression<Date> p = cb.parameter(Date.class, "fromDate");
Path<Date> datePath = jas.get("time");
criteria.add(cb.greaterThanOrEqualTo(datePath, p));
}
if (to != null) {
ParameterExpression<Date> p = cb.parameter(Date.class, "toDate");
Path<Date> datePath = jas.get("time");
criteria.add(cb.lessThanOrEqualTo(datePath, p));
}
if (criteria.size() == 1) {
cq.where(criteria.get(0));
} else {
cq.where(cb.and(criteria.toArray(new Predicate[0])));
}
TypedQuery<JpaAbstractSample> q = em.createQuery(cq);
if (sensorId != null && !"".equals(sensorId)) {
q.setParameter("sensorId", sensorId);
}
if (sampleType != null && !"".equals(sampleType)) {
q.setParameter("sampleType", sampleType);
}
if (from != null) {
q.setParameter("fromDate", from);
}
if (to != null) {
q.setParameter("toDate", to);
}
if (limitCount != null && limitFrom != null) {
q.setFirstResult(limitFrom.intValue());
q.setMaxResults(limitCount.intValue());
}
List<JpaAbstractSample> jass = q.getResultList();
for (Iterator<?> i = jass.iterator(); i.hasNext();) {
JpaAbstractSample u = (JpaAbstractSample) i.next();
samples.add(localSerializer.get().deserialize(u.getValue()));
}
em.close();
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error during samples list retrieving", e);
}
return samples;
}
/*
* (non-Javadoc)
*
* @see
* com.google.developers.gdgfirenze.service.SensormixService#countSamples(
* java.lang.String, java.lang.String, java.util.Date, java.util.Date)
*/
@Override
public long countSamples(String sensorId, String sampleType, Date from, Date to) {
Long retVal = 0L;
try {
if (from != null && to != null && !from.before(to)) {
logger.log(Level.WARNING, "Error from date must be before to date");
} else {
EntityManager em = entityManagerFactory.createEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Object> cq = cb.createQuery();
Root<JpaAbstractSample> jas = cq.from(JpaAbstractSample.class);
cq.multiselect(cb.count(jas));
List<Predicate> criteria = new ArrayList<Predicate>();
if (sensorId != null && !"".equals(sensorId)) {
ParameterExpression<String> p = cb.parameter(String.class, "sensorId");
criteria.add(cb.equal(jas.get("sensorId"), p));
}
if (sampleType != null && !"".equals(sampleType)) {
ParameterExpression<String> p = cb.parameter(String.class, "sampleType");
criteria.add(cb.equal(jas.get("type"), p));
}
if (from != null) {
ParameterExpression<Date> p = cb.parameter(Date.class, "fromDate");
Path<Date> datePath = jas.get("time");
criteria.add(cb.greaterThanOrEqualTo(datePath, p));
}
if (to != null) {
ParameterExpression<Date> p = cb.parameter(Date.class, "toDate");
Path<Date> datePath = jas.get("time");
criteria.add(cb.lessThanOrEqualTo(datePath, p));
}
if (criteria.size() == 1) {
cq.where(criteria.get(0));
} else {
cq.where(cb.and(criteria.toArray(new Predicate[0])));
}
Query q = em.createQuery(cq);
if (sensorId != null && !"".equals(sensorId)) {
q.setParameter("sensorId", sensorId);
}
if (sampleType != null && !"".equals(sampleType)) {
q.setParameter("sampleType", sampleType);
}
if (from != null) {
q.setParameter("fromDate", from);
}
if (to != null) {
q.setParameter("toDate", to);
}
retVal = (Long) q.getSingleResult();
em.close();
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error during samples counting", e);
}
return retVal;
}
/*
* (non-Javadoc)
*
* @see
* com.google.developers.gdgfirenze.service.SensormixService#getSampleReport
* (java.lang.String, java.lang.String, java.util.Date, java.util.Date)
*/
@Override
public SampleReport getSampleReport(String sensorId, String sampleType, Date from, Date to) {
SampleReport sr = new SampleReport();
sr.setSensorId(sensorId);
sr.setSampleType(sampleType);
sr.setDailySampleReports(new ArrayList<DailySampleReport>());
try {
if (from != null && to != null) {
if (!from.before(to)) {
logger.log(Level.WARNING, "Error from date must be before to date");
} else {
EntityManager em = entityManagerFactory.createEntityManager();
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
start.setTime(from);
end.setTime(to);
while (start.before(end)) {
Date internalStart = start.getTime();
start.add(Calendar.DAY_OF_MONTH, 1);
Date internalEnd = start.getTime();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Object> cq = cb.createQuery();
Root<JpaAbstractSample> jas = cq.from(JpaAbstractSample.class);
cq.multiselect(cb.count(jas));
List<Predicate> criteria = new ArrayList<Predicate>();
if (sensorId != null && !"".equals(sensorId)) {
ParameterExpression<String> p = cb.parameter(String.class, "sensorId");
criteria.add(cb.equal(jas.get("sensorId"), p));
}
if (sampleType != null && !"".equals(sampleType)) {
ParameterExpression<String> p = cb.parameter(String.class, "sampleType");
criteria.add(cb.equal(jas.get("type"), p));
}
if (internalStart != null) {
ParameterExpression<Date> p = cb.parameter(Date.class, "fromDate");
Path<Date> datePath = jas.get("time");
criteria.add(cb.greaterThanOrEqualTo(datePath, p));
}
if (internalEnd != null) {
ParameterExpression<Date> p = cb.parameter(Date.class, "toDate");
Path<Date> datePath = jas.get("time");
criteria.add(cb.lessThanOrEqualTo(datePath, p));
}
if (criteria.size() == 1) {
cq.where(criteria.get(0));
} else {
cq.where(cb.and(criteria.toArray(new Predicate[0])));
}
Query q = em.createQuery(cq);
if (sensorId != null && !"".equals(sensorId)) {
q.setParameter("sensorId", sensorId);
}
if (sampleType != null && !"".equals(sampleType)) {
q.setParameter("sampleType", sampleType);
}
if (from != null) {
q.setParameter("fromDate", internalStart);
}
if (to != null) {
q.setParameter("toDate", internalEnd);
}
Long jass = (Long) q.getSingleResult();
DailySampleReport dsr = new DailySampleReport();
dsr.setDate(internalStart);
dsr.setSampleCount(jass);
sr.getDailySampleReports().add(dsr);
}
em.close();
}
} else {
logger.log(Level.WARNING, "Error: no data filter passed.");
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error during samples report creation", e);
}
return sr;
}
/*
* (non-Javadoc)
*
* @see
* com.google.developers.gdgfirenze.service.SensormixService#getSensors(java
* .util.List, java.util.Date, java.util.Date)
*/
@Override
public List<Sensor> getSensors(List<String> sensorIds, Date from, Date to) {
List<Sensor> sensors = new ArrayList<Sensor>();
try {
EntityManager em = entityManagerFactory.createEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<JpaSensor> cq = cb.createQuery(JpaSensor.class);
Root<JpaSensor> js = cq.from(JpaSensor.class);
cq.select(js);
cq.orderBy(cb.desc(js.get("lastSeen")));
List<Predicate> criteria = new ArrayList<Predicate>();
if (sensorIds != null && sensorIds.size() > 0) {
Expression<String> p = js.get("id");
criteria.add(p.in(sensorIds));
}
if (from != null) {
ParameterExpression<Date> p = cb.parameter(Date.class, "fromDate");
Path<Date> datePath = js.get("lastSeen");
criteria.add(cb.greaterThanOrEqualTo(datePath, p));
}
if (to != null) {
ParameterExpression<Date> p = cb.parameter(Date.class, "toDate");
Path<Date> datePath = js.get("lastSeen");
criteria.add(cb.lessThanOrEqualTo(datePath, p));
}
if (criteria.size() == 1) {
cq.where(criteria.get(0));
} else {
cq.where(cb.and(criteria.toArray(new Predicate[0])));
}
TypedQuery<JpaSensor> q = em.createQuery(cq);
if (from != null) {
q.setParameter("fromDate", from);
}
if (to != null) {
q.setParameter("toDate", to);
}
List<JpaSensor> ss = q.getResultList();
for (Iterator<JpaSensor> i = ss.iterator(); i.hasNext();) {
JpaSensor u = i.next();
Sensor s = new Sensor();
s.setId(u.getId());
s.setName(u.getName());
s.setDescription(u.getDescription());
s.setLastSeen(u.getLastSeen());
s.setLat(u.getLat());
s.setLng(u.getLng());
s.setType(u.getType());
sensors.add(s);
}
em.close();
} catch (Exception e) {
logger.log(Level.SEVERE, "Error during sensors list retrieving", e);
}
return sensors;
}
/*
* (non-Javadoc)
*
* @see
* com.google.developers.gdgfirenze.service.SensormixService#registerSensor
* (com.google.developers.gdgfirenze.model.Sensor)
*/
@Override
public void registerSensor(Sensor sensor) {
if (!inMaintenance) {
if (sensor != null && sensor.getId() != null) {
EntityManager em = null;
EntityTransaction tx = null;
try {
em = entityManagerFactory.createEntityManager();
JpaSensor s = em.find(JpaSensor.class, sensor.getId());
if (s == null) {
s = new JpaSensor();
}
s.setId(sensor.getId());
s.setName(sensor.getName());
s.setDescription(sensor.getDescription());
s.setLastSeen(sensor.getLastSeen());
s.setLat(sensor.getLat());
s.setLng(sensor.getLng());
s.setType(sensor.getType());
tx = em.getTransaction();
tx.begin();
em.merge(s);
tx.commit();
} catch (Exception e) {
logger.log(Level.SEVERE, "Error during sensor registration", e);
if (tx.isActive())
tx.rollback();
} finally {
if (em != null && em.isOpen())
em.close();
}
} else {
logger.log(Level.WARNING, "sensor must be not null to register it");
}
} else {
logger.log(Level.INFO, "Cannot register sensor because DataServiceJpaImpl is in maintenace");
throw new RuntimeException(
"Cannot register sensor because DataServiceJpaImpl is in maintenace");
}
}
/*
* (non-Javadoc)
*
* @see
* com.google.developers.gdgfirenze.service.SensormixService#recordSamples
* (java.util.List)
*/
@Override
public void recordSamples(List<AbstractSample> samples) {
if (!inMaintenance) {
if (samples != null) {
List<String> checkList = listSensorsIds();
EntityManager em = null;
EntityTransaction transaction = null;
try {
em = entityManagerFactory.createEntityManager();
transaction = em.getTransaction();
transaction.begin();
for (AbstractSample sample : samples) {
if (!checkList.contains(sample.getSensorId())) {
Sensor s = new Sensor();
s.setId(sample.getSensorId());
s.setName("Unknown");
s.setDescription("Unknown");
s.setLastSeen(sample.getTime());
registerSensor(s);
checkList.add(new String(sample.getSensorId()));
} else {
List<String> sensorList = new ArrayList<String>();
sensorList.add(sample.getSensorId());
Sensor s = getSensors(sensorList, null, null).get(0);
s.setLastSeen(sample.getTime());
registerSensor(s);
}
JpaAbstractSample s = new JpaAbstractSample();
s.setSensorId(sample.getSensorId());
s.setTime(sample.getTime());
s.setType(sample.getType());
s.setValue(localSerializer.get().serialize(sample));
em.persist(s);
}
transaction.commit();
} catch (Exception e) {
logger.log(Level.SEVERE, "Error during samples registration", e);
if (transaction.isActive())
transaction.rollback();
} finally {
if (em != null && em.isOpen())
em.close();
}
} else {
logger.log(Level.WARNING, "samples must be not null to register them");
}
} else {
logger.log(Level.INFO, "Cannot register samples because DataServiceJpaImpl is in maintenace");
throw new RuntimeException(
"Cannot register samples because DataServiceJpaImpl is in maintenace");
}
}
/*
* (non-Javadoc)
*
* @see
* com.google.developers.gdgfirenze.service.SensormixService#listSamplesTypes
* ()
*/
@Override
public List<String> listSamplesTypes() {
List<String> result = new ArrayList<String>();
try {
EntityManager em = entityManagerFactory.createEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<String> cq = cb.createQuery(String.class);
Root<JpaAbstractSample> js = cq.from(JpaAbstractSample.class);
cq.multiselect(js.get("type"));
cq.distinct(true);
TypedQuery<String> q = em.createQuery(cq);
result = q.getResultList();
em.close();
} catch (Exception e) {
logger.log(Level.SEVERE, "Error during samples types retrieving", e);
}
return result;
}
/*
* (non-Javadoc)
*
* @see
* com.google.developers.gdgfirenze.osgi.SensormixAdminInterface#setInMaintenace
* (boolean)
*/
@Override
public void setInMaintenace(boolean value) {
inMaintenance = value;
}
/*
* (non-Javadoc)
*
* @see
* com.google.developers.gdgfirenze.osgi.SensormixAdminInterface#isInMaintenance
* ()
*/
@Override
public boolean isInMaintenance() {
return inMaintenance;
}
}