/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Copyright (c) 2013, MPL CodeInside http://codeinside.ru
*/
package ru.codeinside.gses.service.impl;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.impl.ServiceImpl;
import org.activiti.engine.impl.interceptor.CommandExecutor;
import ru.codeinside.adm.database.DefinitionStatus;
import ru.codeinside.adm.database.Procedure;
import ru.codeinside.adm.database.ProcedureProcessDefinition;
import ru.codeinside.adm.database.ProcedureProcessDefinition_;
import ru.codeinside.adm.database.ProcedureType;
import ru.codeinside.adm.database.Procedure_;
import ru.codeinside.adm.database.Service;
import ru.codeinside.adm.database.Service_;
import ru.codeinside.adm.database.SmevChain;
import ru.codeinside.gses.activiti.SubmitStartFormCommand;
import ru.codeinside.gses.activiti.forms.Signatures;
import ru.codeinside.gses.service.BidID;
import ru.codeinside.gses.service.DeclarantService;
import ru.codeinside.gses.webui.form.SignatureType;
import javax.ejb.Lock;
import javax.ejb.LockType;
import javax.ejb.Singleton;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionManagement;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.SetJoin;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static javax.ejb.TransactionAttributeType.REQUIRED;
@TransactionAttribute(REQUIRED)
@TransactionManagement
@Singleton
@Lock(LockType.READ)
public class DeclarantServiceImpl implements DeclarantService {
@PersistenceContext(unitName = "myPU")
EntityManager em;
@Override
public BidID declare(ProcessEngine engine, String processDefinitionId,
Map<String, Object> properties, Map<SignatureType, Signatures> signatures,
String declarer) {
return commandExecutor(engine).execute(
new SubmitStartFormCommand(null, null, processDefinitionId, properties, signatures, declarer, null, null)
);
}
@Override
public BidID smevDeclare(SmevChain smevChain, String componentName,
ProcessEngine engine, String processDefinitionId,
Map<String, Object> properties, String declarer, String tag) {
return commandExecutor(engine).execute(
new SubmitStartFormCommand(smevChain, componentName, processDefinitionId, properties, null, declarer, tag, null)
);
}
@Override
public List<String> getBids(long gid) {
List<Long> list = em.createQuery("select e.id from Bid e where e.glue.id = :gid", Long.class)
.setParameter("gid", gid).getResultList();
List<String> strings = new ArrayList<String>(list.size());
for (Long id : list) {
strings.add(Long.toString(id));
}
return strings;
}
@Override
public long getGlueIdByRequestIdRef(String requestIdRef) {
List<Long> rs = em.createQuery(
"select s.id from ExternalGlue s where s.requestIdRef=:requestIdRef", Long.class)
.setParameter("requestIdRef", requestIdRef)
.getResultList();
if (rs.isEmpty()) {
return 0L;
}
return rs.get(0);
}
@Override
public int activeProceduresCount(ProcedureType type, long serviceId) {
return proceduresCount(type, serviceId, true);
}
@Override
public List<Procedure> selectActiveProcedures(ProcedureType type, long serviceId, int start, int count) {
return selectProcedures(type, serviceId, start, count, true);
}
@Override
public List<Procedure> selectDeclarantProcedures(ProcedureType type, long serviceId, int start, int count) {
return selectProcedures(type, serviceId, start, count, false);
}
@Override
public int activeServicesCount(ProcedureType type) {
CriteriaBuilder b = em.getCriteriaBuilder();
CriteriaQuery<Number> query = b.createQuery(Number.class);
Root<Service> service = query.from(Service.class);
query.select(b.countDistinct(service))
.where(typeAndStatus(type, b, service));
return count(query);
}
@Override
public List<Service> selectActiveServices(ProcedureType type, int start, int count) {
CriteriaBuilder b = em.getCriteriaBuilder();
CriteriaQuery<Service> query = b.createQuery(Service.class);
Root<Service> service = query.from(Service.class);
query.select(service)
.where(typeAndStatus(type, b, service))
.distinct(true)
.orderBy(b.asc(service.get(Service_.name)));
return chunk(start, count, query);
}
@Override
public ProcedureProcessDefinition selectActive(long procedureId) {
CriteriaBuilder b = em.getCriteriaBuilder();
CriteriaQuery<ProcedureProcessDefinition> query = b.createQuery(ProcedureProcessDefinition.class);
Root<ProcedureProcessDefinition> def = query.from(ProcedureProcessDefinition.class);
Path<Procedure> procedure = def.get(ProcedureProcessDefinition_.procedure);
query.select(def).where(
b.and(
b.equal(procedure.get(Procedure_.id), procedureId),
b.equal(def.get(ProcedureProcessDefinition_.status), DefinitionStatus.Work)
)
);
List<ProcedureProcessDefinition> defs = em.createQuery(query).setMaxResults(1).getResultList();
return defs.isEmpty() ? null : defs.get(0);
}
// ---- internals ----
private CommandExecutor commandExecutor(ProcessEngine engine) {
return ((ServiceImpl) engine.getFormService()).getCommandExecutor();
}
private int proceduresCount(ProcedureType type, long serviceId, boolean usePathToArchive) {
CriteriaBuilder b = em.getCriteriaBuilder();
CriteriaQuery<Number> query = b.createQuery(Number.class);
Root<Procedure> procedures = query.from(Procedure.class);
query.select(b.countDistinct(procedures))
.where(typeAndService(type, serviceId, b, procedures, usePathToArchive));
return count(query);
}
private List<Procedure> selectProcedures(ProcedureType type, long serviceId, int start, int count, boolean usePathToArchive) {
CriteriaBuilder b = em.getCriteriaBuilder();
CriteriaQuery<Procedure> query = b.createQuery(Procedure.class);
Root<Procedure> procedures = query.from(Procedure.class);
query.select(procedures)
.distinct(true)
.where(typeAndService(type, serviceId, b, procedures, usePathToArchive))
.orderBy(b.asc(procedures.get(Procedure_.name)));
return chunk(start, count, query);
}
private <T> List<T> chunk(int start, int count, CriteriaQuery<T> query) {
return em.createQuery(query).setFirstResult(start).setMaxResults(count).getResultList();
}
private int count(final CriteriaQuery<Number> query) {
return em.createQuery(query).getSingleResult().intValue();
}
private Predicate typeAndService(ProcedureType type, long serviceId, CriteriaBuilder b,
Root<Procedure> procedure, boolean findInPathToArchive) {
Predicate typeAndStatus = typeAndStatus(type, b, procedure, findInPathToArchive);
if (serviceId < 0) {
return typeAndStatus;
}
Path<Service> service = procedure.get(Procedure_.service);
return b.and(
typeAndStatus,
b.equal(service.get(Service_.id), serviceId)
);
}
private Predicate typeAndStatus(ProcedureType type, CriteriaBuilder b, Root<Service> service) {
return typeAndStatus(type, b, service.join(Service_.procedures), true);
}
private Predicate typeAndStatus(ProcedureType type, CriteriaBuilder b,
From<?, Procedure> procedures, boolean findInPathToArchive) {
SetJoin<Procedure, ProcedureProcessDefinition> defs = procedures.join(Procedure_.processDefinitions);
if (findInPathToArchive) {
return b.and(
b.equal(procedures.get(Procedure_.type), type),
b.or(
b.equal(defs.get(ProcedureProcessDefinition_.status), DefinitionStatus.Work),
b.equal(defs.get(ProcedureProcessDefinition_.status), DefinitionStatus.PathToArchive)
)
);
}
return b.and(
b.equal(procedures.get(Procedure_.type), type),
b.equal(defs.get(ProcedureProcessDefinition_.status), DefinitionStatus.Work)
);
}
}