/**
* Licensed to the Austrian Association for Software Tool Integration (AASTI)
* under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright
* ownership. The AASTI licenses this file to you 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.openengsb.core.edb.jpa.internal.dao;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;
import org.openengsb.core.api.model.CommitMetaInfo;
import org.openengsb.core.api.model.CommitQueryRequest;
import org.openengsb.core.api.model.QueryRequest;
import org.openengsb.core.edb.api.EDBException;
import org.openengsb.core.edb.jpa.internal.JPACommit;
import org.openengsb.core.edb.jpa.internal.JPAHead;
import org.openengsb.core.edb.jpa.internal.JPAObject;
import org.openengsb.core.edb.jpa.internal.util.QueryRequestCriteriaBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Iterables;
public class DefaultJPADao implements JPADao {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultJPADao.class);
private EntityManager entityManager;
public DefaultJPADao() {
}
public DefaultJPADao(EntityManager entityManager) {
this.entityManager = entityManager;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public JPAHead getJPAHead(long timestamp) throws EDBException {
synchronized (entityManager) {
LOGGER.debug("Loading head for timestamp {}", timestamp);
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<JPAObject> query = criteriaBuilder.createQuery(JPAObject.class);
Root<JPAObject> from = query.from(JPAObject.class);
query.select(from);
Subquery<Number> subquery = query.subquery(Number.class);
Root maxTime = subquery.from(JPAObject.class);
subquery.select(criteriaBuilder.max(maxTime.get("timestamp")));
Predicate subPredicate1 = criteriaBuilder.le(maxTime.get("timestamp"), timestamp);
Predicate subPredicate2 = criteriaBuilder.equal(maxTime.get("oid"), from.get("oid"));
subquery.where(criteriaBuilder.and(subPredicate1, subPredicate2));
Predicate predicate1 = criteriaBuilder.equal(from.get("timestamp"), subquery);
Predicate predicate2 = criteriaBuilder.notEqual(from.get("isDeleted"), Boolean.TRUE);
query.where(criteriaBuilder.and(predicate1, predicate2));
TypedQuery<JPAObject> typedQuery = entityManager.createQuery(query);
List<JPAObject> resultList = typedQuery.getResultList();
JPAHead head = new JPAHead();
head.setJPAObjects(resultList);
head.setTimestamp(timestamp);
return head;
}
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public List<JPAObject> getJPAObjectHistory(String oid) throws EDBException {
synchronized (entityManager) {
LOGGER.debug("Loading the history for the object {}", oid);
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<JPAObject> query = criteriaBuilder.createQuery(JPAObject.class);
Root from = query.from(JPAObject.class);
query.select(from);
query.where(criteriaBuilder.equal(from.get("oid"), oid));
query.orderBy(criteriaBuilder.asc(from.get("timestamp")));
TypedQuery<JPAObject> typedQuery = entityManager.createQuery(query);
return typedQuery.getResultList();
}
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public List<JPAObject> getJPAObjectHistory(String oid, long from, long to) throws EDBException {
synchronized (entityManager) {
LOGGER.debug("Loading the history for the object {} from {} to {}", new Object[]{ oid, from, to });
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<JPAObject> query = criteriaBuilder.createQuery(JPAObject.class);
Root f = query.from(JPAObject.class);
query.select(f);
Predicate predicate1 = criteriaBuilder.equal(f.get("oid"), oid);
Predicate predicate2 = criteriaBuilder.between(f.get("timestamp"), from, to);
query.where(criteriaBuilder.and(predicate1, predicate2));
query.orderBy(criteriaBuilder.asc(f.get("timestamp")));
TypedQuery<JPAObject> typedQuery = entityManager.createQuery(query);
return typedQuery.getResultList();
}
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public JPAObject getJPAObject(String oid, long timestamp) throws EDBException {
synchronized (entityManager) {
LOGGER.debug("Loading object {} for the time {}", oid, timestamp);
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<JPAObject> query = criteriaBuilder.createQuery(JPAObject.class);
Root from = query.from(JPAObject.class);
query.select(from);
Predicate predicate1 = criteriaBuilder.equal(from.get("oid"), oid);
Predicate predicate2 = criteriaBuilder.le(from.get("timestamp"), timestamp);
query.where(criteriaBuilder.and(predicate1, predicate2));
query.orderBy(criteriaBuilder.desc(from.get("timestamp")));
TypedQuery<JPAObject> typedQuery = entityManager.createQuery(query).setMaxResults(1);
List<JPAObject> resultList = typedQuery.getResultList();
if (resultList.size() < 1) {
throw new EDBException("Failed to query existing object");
} else if (resultList.size() > 1) {
throw new EDBException("Received more than 1 object which should not be possible!");
}
return resultList.get(0);
}
}
@Override
public JPAObject getJPAObject(String oid) throws EDBException {
synchronized (entityManager) {
LOGGER.debug("Loading newest object {}", oid);
return getJPAObject(oid, System.currentTimeMillis());
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public List<JPAObject> getJPAObjects(List<String> oid) throws EDBException {
synchronized (entityManager) {
LOGGER.debug("Loading newest object {}", oid);
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<JPAObject> query = criteriaBuilder.createQuery(JPAObject.class);
Root<JPAObject> from = query.from(JPAObject.class);
query.select(from);
Subquery<Number> subquery = query.subquery(Number.class);
Root maxTime = subquery.from(JPAObject.class);
subquery.select(criteriaBuilder.max(maxTime.get("timestamp")));
subquery.where(criteriaBuilder.equal(from.get("oid"), maxTime.get("oid")));
Predicate predicate1 = criteriaBuilder.in(from.get("oid")).value(oid);
Predicate predicate2 = criteriaBuilder.equal(from.get("timestamp"), subquery);
query.where(criteriaBuilder.and(predicate1, predicate2));
TypedQuery<JPAObject> typedQuery = entityManager.createQuery(query);
List<JPAObject> resultList = typedQuery.getResultList();
return resultList;
}
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public List<JPACommit> getJPACommit(String oid, long from, long to) throws EDBException {
synchronized (entityManager) {
LOGGER.debug("Loading all commits which involve object {} from {} to {}", new Object[]{ oid, from, to });
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<JPACommit> query = criteriaBuilder.createQuery(JPACommit.class);
Root<JPACommit> f = query.from(JPACommit.class);
query.select(f);
Subquery<JPAObject> subquery = query.subquery(JPAObject.class);
Root fromJPAObject = subquery.from(JPAObject.class);
subquery.select(fromJPAObject.get("timestamp"));
Predicate predicate1 = criteriaBuilder.equal(fromJPAObject.get("oid"), oid);
Predicate predicate2 = criteriaBuilder.between(fromJPAObject.get("timestamp"), from, to);
subquery.where(criteriaBuilder.and(predicate1, predicate2));
query.where(criteriaBuilder.in(f.get("timestamp")).value(subquery));
query.orderBy(criteriaBuilder.asc(f.get("timestamp")));
TypedQuery<JPACommit> typedQuery = entityManager.createQuery(query);
return typedQuery.getResultList();
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public List<String> getResurrectedOIDs() throws EDBException {
synchronized (entityManager) {
LOGGER.debug("get resurrected JPA objects");
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<String> query = criteriaBuilder.createQuery(String.class);
Root from = query.from(JPAObject.class);
query.select(from.get("oid"));
Subquery<JPAObject> sub = query.subquery(JPAObject.class);
Root f = sub.from(JPAObject.class);
sub.select(f);
Predicate subPredicate1 = criteriaBuilder.equal(from.get("oid"), f.get("oid"));
Predicate subPredicate2 = criteriaBuilder.equal(f.get("isDeleted"), Boolean.TRUE);
Predicate subPredicate3 = criteriaBuilder.gt(from.get("timestamp"), f.get("timestamp"));
sub.where(criteriaBuilder.and(subPredicate1, subPredicate2, subPredicate3));
Predicate predicate1 = criteriaBuilder.notEqual(from.get("isDeleted"), Boolean.TRUE);
Predicate predicate2 = criteriaBuilder.exists(sub);
query.where(predicate1, predicate2);
TypedQuery<String> typedQuery = entityManager.createQuery(query);
return typedQuery.getResultList();
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public List<JPACommit> getJPACommit(long timestamp) throws EDBException {
synchronized (entityManager) {
LOGGER.debug("Load the commit for the timestamp {}", timestamp);
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<JPACommit> query = criteriaBuilder.createQuery(JPACommit.class);
Root<JPACommit> from = query.from(JPACommit.class);
query.select(from);
Subquery<Number> subquery = query.subquery(Number.class);
Root maxTime = subquery.from(JPACommit.class);
subquery.select(criteriaBuilder.max(maxTime.get("timestamp")));
subquery.where(criteriaBuilder.le(maxTime.get("timestamp"), timestamp));
query.where(criteriaBuilder.equal(from.get("timestamp"), subquery));
TypedQuery<JPACommit> typedQuery = entityManager.createQuery(query);
return typedQuery.getResultList();
}
}
@Override
public JPACommit getJPACommit(String revision) throws EDBException {
synchronized (entityManager) {
LOGGER.debug("Get commit for the revision {}", revision);
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<JPACommit> query = criteriaBuilder.createQuery(JPACommit.class);
Root<JPACommit> from = query.from(JPACommit.class);
query.select(from).where(criteriaBuilder.equal(from.get("revision"), revision));
TypedQuery<JPACommit> typedQuery = entityManager.createQuery(query);
List<JPACommit> result = typedQuery.getResultList();
switch (result.size()) {
case 0:
throw new EDBException("There is no commit with the given revision " + revision);
case 1:
return result.get(0);
default:
throw new EDBException("More than one commit with the given revision found!");
}
}
}
@Override
public List<JPACommit> getCommits(Map<String, Object> param) throws EDBException {
synchronized (entityManager) {
LOGGER.debug("Get commits which are given to a param map with {} elements", param.size());
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<JPACommit> query = criteriaBuilder.createQuery(JPACommit.class);
Root<JPACommit> from = query.from(JPACommit.class);
query.select(from);
Predicate[] predicates = analyzeParamMap(criteriaBuilder, from, param);
query.where(criteriaBuilder.and(predicates));
TypedQuery<JPACommit> typedQuery = entityManager.createQuery(query);
return typedQuery.getResultList();
}
}
@Override
public JPACommit getLastCommit(Map<String, Object> param) throws EDBException {
synchronized (entityManager) {
LOGGER.debug("Get last commit which are given to a param map with {} elements", param.size());
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<JPACommit> query = criteriaBuilder.createQuery(JPACommit.class);
Root<JPACommit> from = query.from(JPACommit.class);
query.select(from);
Predicate[] predicates = analyzeParamMap(criteriaBuilder, from, param);
query.where(criteriaBuilder.and(predicates));
query.orderBy(criteriaBuilder.desc(from.get("timestamp")));
TypedQuery<JPACommit> typedQuery = entityManager.createQuery(query).setMaxResults(1);
try {
return typedQuery.getSingleResult();
} catch (NoResultException ex) {
throw new EDBException("there was no Object found with the given query parameters", ex);
}
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public List<CommitMetaInfo> getRevisionsOfMatchingCommits(CommitQueryRequest request) throws EDBException {
synchronized (entityManager) {
LOGGER.debug("Get matching revisions for the request {}", request);
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery query = criteriaBuilder.createQuery();
Root<JPACommit> from = query.from(JPACommit.class);
query.multiselect(from.get("committer"), from.get("timestamp"), from.get("context"), from.get("comment"),
from.get("revision"), from.get("parent"), from.get("domainId"), from.get("connectorId"),
from.get("instanceId"));
Predicate[] predicates = convertCommitRequestToPredicates(criteriaBuilder, from, request);
query.where(criteriaBuilder.and(predicates));
query.orderBy(criteriaBuilder.asc(from.get("timestamp")));
TypedQuery<Object[]> typedQuery = entityManager.createQuery(query);
List<CommitMetaInfo> infos = new ArrayList<>();
for (Object[] row : typedQuery.getResultList()) {
CommitMetaInfo info = new CommitMetaInfo();
info.setCommitter(row[0] != null ? row[0].toString() : null);
info.setTimestamp(row[1] != null ? Long.valueOf(row[1].toString()) : null);
info.setContext(row[2] != null ? row[2].toString() : null);
info.setComment(row[3] != null ? row[3].toString() : null);
info.setRevision(row[4] != null ? row[4].toString() : null);
info.setParent(row[5] != null ? row[5].toString() : null);
info.setDomainId(row[6] != null ? row[6].toString() : null);
info.setConnectorId(row[7] != null ? row[7].toString() : null);
info.setInstanceId(row[8] != null ? row[8].toString() : null);
infos.add(info);
}
return infos;
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Predicate[] convertCommitRequestToPredicates(CriteriaBuilder builder, Root from,
CommitQueryRequest request) {
List<Predicate> predicates = new ArrayList<>();
if (request.getCommitter() != null) {
predicates.add(builder.equal(from.get("committer"), request.getCommitter()));
}
if (request.getContext() != null) {
predicates.add(builder.equal(from.get("context"), request.getContext()));
}
predicates
.add(builder.between(from.get("timestamp"), request.getStartTimestamp(), request.getEndTimestamp()));
return Iterables.toArray(predicates, Predicate.class);
}
/**
* Analyzes the map and filters the values which are used for query
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private Predicate[] analyzeParamMap(CriteriaBuilder criteriaBuilder, Root from, Map<String, Object> param) {
List<Predicate> predicates = new ArrayList<Predicate>();
for (Map.Entry<String, Object> entry : param.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (key.equals("timestamp")) {
predicates.add(criteriaBuilder.le(from.get("timestamp"), (Long) value));
} else if (key.equals("committer")) {
predicates.add(criteriaBuilder.equal(from.get("committer"), value));
} else if (key.equals("context")) {
predicates.add(criteriaBuilder.equal(from.get("context"), value));
}
}
Predicate[] temp = new Predicate[predicates.size()];
for (int i = 0; i < predicates.size(); i++) {
temp[i] = predicates.get(i);
}
return temp;
}
@Override
public Integer getVersionOfOid(String oid) throws EDBException {
synchronized (entityManager) {
LOGGER.debug("loading version of model under the oid {}", oid);
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> query = criteriaBuilder.createQuery(Long.class);
Root<JPAObject> from = query.from(JPAObject.class);
Expression<Long> maxExpression = criteriaBuilder.count(from.get("oid"));
query.select(maxExpression);
query.where(criteriaBuilder.equal(from.get("oid"), oid));
TypedQuery<Long> typedQuery = entityManager.createQuery(query);
try {
return (int) typedQuery.getSingleResult().longValue();
} catch (NoResultException ex) {
LOGGER.debug("no model under the oid {}. Returning 0", oid);
return 0;
}
}
}
@Override
public List<JPAObject> query(QueryRequest request) throws EDBException {
synchronized (entityManager) {
LOGGER.debug("Perform query with the query object: {}", request);
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
QueryRequestCriteriaBuilder builder = new QueryRequestCriteriaBuilder(request, criteriaBuilder);
TypedQuery<JPAObject> typedQuery = entityManager.createQuery(builder.buildQuery());
return typedQuery.getResultList();
}
}
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
}