Java tutorial
/* * Copyright (C) 2016 Singular Studios (a.k.a Atom Tecnologia) - www.opensingular.com * * 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.opensingular.flow.persistence.service; import com.google.common.base.Throwables; import org.apache.commons.lang3.StringUtils; import org.hibernate.Criteria; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.Order; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Restrictions; import org.hibernate.criterion.Subqueries; import org.opensingular.flow.core.Flow; import org.opensingular.flow.core.SUser; import org.opensingular.flow.core.SingularFlowException; import org.opensingular.flow.core.TaskInstance; import org.opensingular.flow.core.TaskType; import org.opensingular.flow.core.entity.IEntityByCod; import org.opensingular.flow.core.entity.IEntityCategory; import org.opensingular.flow.core.entity.IEntityExecutionVariable; import org.opensingular.flow.core.entity.IEntityProcessDefinition; import org.opensingular.flow.core.entity.IEntityProcessInstance; import org.opensingular.flow.core.entity.IEntityProcessVersion; import org.opensingular.flow.core.entity.IEntityRoleDefinition; import org.opensingular.flow.core.entity.IEntityRoleInstance; import org.opensingular.flow.core.entity.IEntityTaskDefinition; import org.opensingular.flow.core.entity.IEntityTaskHistoricType; import org.opensingular.flow.core.entity.IEntityTaskInstance; import org.opensingular.flow.core.entity.IEntityTaskInstanceHistory; import org.opensingular.flow.core.entity.IEntityTaskTransitionVersion; import org.opensingular.flow.core.entity.IEntityTaskVersion; import org.opensingular.flow.core.entity.IEntityVariableInstance; import org.opensingular.flow.core.entity.IEntityVariableType; import org.opensingular.flow.core.service.IPersistenceService; import org.opensingular.flow.core.variable.VarInstance; import org.opensingular.flow.core.variable.VarInstanceMap; import org.opensingular.flow.core.variable.VarType; import org.opensingular.flow.persistence.entity.util.SessionLocator; import org.opensingular.flow.persistence.entity.util.SessionWrapper; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.ListIterator; import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; public abstract class AbstractHibernatePersistenceService<DEFINITION_CATEGORY extends IEntityCategory, PROCESS_DEF extends IEntityProcessDefinition, PROCESS_VERSION extends IEntityProcessVersion, PROCESS_INSTANCE extends IEntityProcessInstance, TASK_INSTANCE extends IEntityTaskInstance, TASK_DEF extends IEntityTaskDefinition, TASK_VERSION extends IEntityTaskVersion, VARIABLE_INSTANCE extends IEntityVariableInstance, PROCESS_ROLE extends IEntityRoleDefinition, ROLE_USER extends IEntityRoleInstance> extends AbstractHibernateService implements IPersistenceService<DEFINITION_CATEGORY, PROCESS_DEF, PROCESS_VERSION, PROCESS_INSTANCE, TASK_INSTANCE, TASK_DEF, TASK_VERSION, VARIABLE_INSTANCE, PROCESS_ROLE, ROLE_USER> { public AbstractHibernatePersistenceService(SessionLocator sessionLocator) { super(sessionLocator); } // ------------------------------------------------------- // ProcessIntance // ------------------------------------------------------- @Override @Nonnull public Optional<PROCESS_INSTANCE> retrieveProcessInstanceByCod(@Nonnull Integer cod) { Objects.requireNonNull(cod); return getSession().retrieve(getClassProcessInstance(), cod); } /** * Cria uma intancia de ProcessIntance parcialmente preenchida. Apenas isola * a persistencia do tipo correto. */ protected abstract PROCESS_INSTANCE newProcessInstance(PROCESS_VERSION process); @Override public PROCESS_INSTANCE createProcessInstance(PROCESS_VERSION process, TASK_VERSION initialState) { PROCESS_INSTANCE processInstance = newProcessInstance(process); processInstance.setBeginDate(new Date()); return processInstance; } @Override public PROCESS_INSTANCE saveProcessInstance(PROCESS_INSTANCE instancia) { SessionWrapper sw = getSession(); sw.saveOrUpdate(instancia); sw.refresh(instancia); return instancia; } @Override public void setProcessInstanceParent(PROCESS_INSTANCE instance, PROCESS_INSTANCE parentTask) { throw new UnsupportedOperationException("Apenas o AlocPro tem suporte a instncia pai."); } protected abstract ROLE_USER newEntityRole(PROCESS_INSTANCE instance, PROCESS_ROLE role, SUser user, SUser allocator); @Override public ROLE_USER setInstanceUserRole(PROCESS_INSTANCE instance, PROCESS_ROLE role, SUser user) { SUser resolvedUser = saveUserIfNeeded(user); ROLE_USER entityRole = newEntityRole(instance, role, resolvedUser, Flow.getUserIfAvailable()); SessionWrapper sw = getSession(); sw.save(entityRole); sw.refresh(instance); return entityRole; } @Override public void removeInstanceUserRole(PROCESS_INSTANCE processInstance, ROLE_USER roleInstance) { SessionWrapper sw = getSession(); sw.delete(roleInstance); sw.refresh(processInstance); } public SUser saveUserIfNeeded(SUser sUser) { return Flow.getConfigBean().getUserService().saveUserIfNeeded(sUser); } protected abstract Class<TASK_INSTANCE> getClassTaskInstance(); /** * Cria uma nova taskInstance parcialmente preenchiada apenas com * processIntance e taskVersion. */ protected abstract TASK_INSTANCE newTaskInstance(PROCESS_INSTANCE processInstance, TASK_VERSION taskVersion); @Override @Nonnull public Optional<TASK_INSTANCE> retrieveTaskInstanceByCod(@Nonnull Integer cod) { Objects.requireNonNull(cod); return getSession().retrieve(getClassTaskInstance(), cod); } @Override public TASK_INSTANCE addTask(PROCESS_INSTANCE processInstance, TASK_VERSION taskVersion) { Date agora = new Date(); TASK_INSTANCE taskInstance = newTaskInstance(processInstance, taskVersion); taskInstance.setBeginDate(agora); if (taskVersion.isEnd()) { processInstance.setEndDate(agora); taskInstance.setEndDate(agora); } else { processInstance.setEndDate(null); } processInstance.addTask(taskInstance); SessionWrapper sw = getSession(); sw.save(taskInstance); sw.update(processInstance); sw.refresh(processInstance); return taskInstance; } @Override public void setParentTask(PROCESS_INSTANCE childrenInstance, TASK_INSTANCE parentTask) { childrenInstance.setParentTask(parentTask); getSession().update(childrenInstance); } @Override public void updateTask(TASK_INSTANCE tarefa) { getSession().update(tarefa); } @Override public void completeTask(TASK_INSTANCE task, String transitionAbbreviation, SUser responsibleUser) { SUser resolvedUser = saveUserIfNeeded(responsibleUser); task.setEndDate(new Date()); IEntityTaskTransitionVersion transition = task.getTaskVersion().getTransition(transitionAbbreviation); task.setExecutedTransition(transition); if (resolvedUser != null) { task.setResponsibleUser(resolvedUser); } getSession().update(task); } @Override public void updateTargetEndDate(TASK_INSTANCE taskInstance, Date targetEndDate) { taskInstance.setTargetEndDate(targetEndDate); updateTask(taskInstance); } @Override public void relocateTask(TASK_INSTANCE taskInstance, SUser user) { taskInstance.setAllocatedUser(saveUserIfNeeded(user)); updateTask(taskInstance); } protected abstract IEntityTaskInstanceHistory newTaskInstanceHistory(TASK_INSTANCE task, IEntityTaskHistoricType taskHistoryType, SUser allocatedUser, SUser responsibleUser); @Override public IEntityTaskInstanceHistory saveTaskHistoricLog(TASK_INSTANCE task, String typeDescription, String detail, SUser allocatedUser, SUser responsibleUser, Date dateHour, PROCESS_INSTANCE generatedProcessInstance) { IEntityTaskHistoricType taskHistoryType = retrieveOrCreateTaskHistoricType(typeDescription); SUser resolvedUser = saveUserIfNeeded(responsibleUser); IEntityTaskInstanceHistory history = newTaskInstanceHistory(task, taskHistoryType, allocatedUser, resolvedUser); history.setBeginDateAllocation(dateHour == null ? new Date() : dateHour); history.setDescription(detail); SessionWrapper sw = getSession(); sw.save(history); sw.refresh(task); return history; } protected abstract Class<? extends IEntityTaskHistoricType> getClassEntityTaskHistoricType(); protected final IEntityTaskHistoricType retrieveOrCreateTaskHistoricType(String typeDescription) { SessionWrapper sw = getSession(); Class<? extends IEntityTaskHistoricType> classe = getClassEntityTaskHistoricType(); IEntityTaskHistoricType variableType = sw.retrieveFirstFromCachedRetriveAll(classe, vt -> vt.getDescription().equals(typeDescription)); if (variableType == null) { try { variableType = classe.newInstance(); } catch (Exception e) { throw Throwables.propagate(e); } variableType.setDescription(typeDescription); sw.save(variableType); } return variableType; } // ------------------------------------------------------- // Versions // ------------------------------------------------------- @Override public PROCESS_VERSION saveProcessVersion(PROCESS_VERSION processVersion) { SessionWrapper sw = getSession(); sw.saveOrUpdate(processVersion.getProcessDefinition()); sw.saveOrUpdate(processVersion); sw.saveOrUpdate(processVersion.getVersionTasks().stream().map(tv -> tv.getTaskDefinition())); sw.saveOrUpdate(processVersion.getVersionTasks().stream() .flatMap(tv -> tv.getTaskDefinition().getRolesTask() != null ? tv.getTaskDefinition().getRolesTask().stream() : Stream.empty())); sw.saveOrUpdate(processVersion.getVersionTasks()); sw.saveOrUpdate(processVersion.getVersionTasks().stream().flatMap(tv -> tv.getTransitions().stream())); sw.refresh(processVersion.getProcessDefinition()); return retrieveProcessVersionByCod(processVersion.getCod()); } // ------------------------------------------------------- // Variable // ------------------------------------------------------- @Nonnull protected abstract Optional<VARIABLE_INSTANCE> retrieveVariableInstanceByCod(@Nonnull Integer cod); @Nonnull protected VARIABLE_INSTANCE retrieveVariableInstanceByCodOrException(@Nonnull Integer cod) { return retrieveVariableInstanceByCod(cod) .orElseThrow(() -> new SingularFlowException("No foi encontrado a varivel cod=" + cod)); } protected abstract VARIABLE_INSTANCE newVariableInstance(PROCESS_INSTANCE processInstance, String name); @Override @Nullable public Integer updateVariableValue(@Nonnull PROCESS_INSTANCE processInstance, @Nonnull VarInstance mVariavel, @Nullable Integer dbVariableCod) { SessionWrapper ss = getSession(); Object valorAjustado = mVariavel.getValue(); VARIABLE_INSTANCE variavel = null; if (dbVariableCod != null) { variavel = retrieveVariableInstanceByCodOrException(dbVariableCod); } if (valorAjustado == null || isVazio(valorAjustado)) { if (variavel != null) { variavel.setValue(null); ss.merge(variavel); ss.refresh(processInstance); } else { return null; } } else if (variavel == null) { // Para no forar carga variavel = newVariableInstance(processInstance, mVariavel.getRef()); String valorString = mVariavel.getPersistentString(); if (!Objects.equals(valorString, variavel.getValue())) { variavel.setType(retrieveOrCreateEntityVariableType(mVariavel.getType())); variavel.setValue(valorString); } ss.save(variavel); ss.refresh(processInstance); } else { String valorString = mVariavel.getPersistentString(); if (!Objects.equals(valorString, variavel.getValue())) { variavel.setType(retrieveOrCreateEntityVariableType(mVariavel.getType())); variavel.setValue(valorString); ss.merge(variavel); } } return variavel.getCod(); } protected abstract IEntityExecutionVariable newExecutionVariable(PROCESS_INSTANCE instance, IEntityVariableInstance processInstanceVar, TASK_INSTANCE originTask, TASK_INSTANCE destinationTask, IEntityVariableType type); @Override public void saveVariableHistoric(Date dateHour, PROCESS_INSTANCE instance, TASK_INSTANCE originTask, TASK_INSTANCE destinationTask, VarInstanceMap<?, ?> instanceMap) { if (instanceMap == null) { return; } SessionWrapper ss = getSession(); boolean salvou = false; for (VarInstance variavel : instanceMap) { if (variavel.getValue() != null) { try { saveOneVariable(ss, instance, originTask, destinationTask, variavel, dateHour); } catch (Exception e) { throw SingularFlowException .rethrow("Erro ao salvar varivel '" + variavel.getName() + "' no histrico", e); } salvou = true; } } if (salvou) { if (originTask != null) { ss.refresh(originTask); } if (destinationTask != null) { ss.refresh(destinationTask); } } } private void saveOneVariable(SessionWrapper ss, PROCESS_INSTANCE instance, TASK_INSTANCE originTask, TASK_INSTANCE destinationTask, VarInstance variavel, Date dateHour) { boolean salvou; IEntityVariableType type = retrieveOrCreateEntityVariableType(variavel.getType()); String ref = variavel.getRef(); IEntityVariableInstance processInstanceVar = instance.getVariable(ref); IEntityExecutionVariable novo = newExecutionVariable(instance, processInstanceVar, originTask, destinationTask, type); novo.setName(ref); novo.setValue(variavel.getPersistentString()); novo.setDate(dateHour); ss.save(novo); } protected abstract Class<? extends IEntityVariableType> getClassEntityVariableType(); @Override public final IEntityVariableType retrieveOrCreateEntityVariableType(VarType varType) { SessionWrapper sw = getSession(); Class<? extends IEntityVariableType> classe = getClassEntityVariableType(); String typeClassName = varType.getClass().getName(); IEntityVariableType variableType = sw.retrieveFirstFromCachedRetriveAll(classe, vt -> vt.getTypeClassName().equals(typeClassName)); if (variableType == null) { try { variableType = classe.newInstance(); } catch (Exception e) { throw Throwables.propagate(e); } variableType.setDescription(varType.getName()); variableType.setTypeClassName(typeClassName); sw.save(variableType); } return variableType; } protected abstract Class<PROCESS_INSTANCE> getClassProcessInstance(); public List<PROCESS_INSTANCE> retrieveProcessInstancesWith(PROCESS_DEF process, Date dataInicio, Date dataFim, java.util.Collection<? extends TASK_DEF> states) { Objects.requireNonNull(process); final Criteria c = getSession().createCriteria(getClassProcessInstance(), "PI"); c.createAlias("PI.processVersion", "DEF"); c.add(Restrictions.eq("DEF.processDefinition", process)); if (states != null && !states.isEmpty()) { DetachedCriteria sub = DetachedCriteria.forClass(getClassTaskInstance(), "T"); sub.add(Restrictions.eqProperty("T.processInstance.cod", "PI.cod")); sub.createAlias("T.task", "TV"); sub.add(Restrictions.in("TV.taskDefinition", states)); sub.add(Restrictions.isNull("T.endDate")); sub.setProjection(Projections.id()); c.add(Subqueries.exists(sub)); } if (dataInicio != null && dataFim != null) { c.add(Restrictions.or( Restrictions.and(Restrictions.ge("PI.beginDate", dataInicio), Restrictions.lt("PI.beginDate", dataFim)), Restrictions.and(Restrictions.ge("PI.endDate", dataInicio), Restrictions.lt("PI.endDate", dataFim)), Restrictions.and(Restrictions.lt("PI.beginDate", dataInicio), Restrictions.ge("PI.endDate", dataInicio)), Restrictions.and(Restrictions.isNull("PI.endDate"), Restrictions.lt("PI.beginDate", dataFim)))); } else if (dataInicio != null) { c.add(Restrictions.or(Restrictions.ge("PI.beginDate", dataInicio), Restrictions.ge("PI.endDate", dataInicio), Restrictions .and(Restrictions.lt("PI.beginDate", dataInicio), Restrictions.isNull("PI.endDate")))); } else if (dataFim != null) { c.add(Restrictions.or(Restrictions.le("PI.beginDate", dataFim), Restrictions.le("PI.endDate", dataFim))); } c.addOrder(Order.desc("PI.beginDate")); return c.list(); } public List<PROCESS_INSTANCE> retrieveProcessInstancesWith(PROCESS_DEF process, SUser creatingUser, Boolean active) { Objects.requireNonNull(process); Criteria c = getSession().createCriteria(getClassProcessInstance(), "PI"); c.createAlias("PI.processVersion", "DEF"); c.add(Restrictions.eq("DEF.processDefinition", process)); if (active != null) { DetachedCriteria sub = DetachedCriteria.forClass(getClassTaskInstance(), "T"); sub.createAlias("T.task", "TA"); sub.add(Restrictions.eqProperty("T.processInstance.cod", "PI.cod")); sub.add(Restrictions.isNull("T.endDate")); if (active) { sub.add(Restrictions.ne("TA.type", TaskType.END)); } else { sub.add(Restrictions.eq("TA.type", TaskType.END)); } sub.setProjection(Projections.id()); c.add(Subqueries.exists(sub)); } if (creatingUser != null) { c.add(Restrictions.eq("PI.userCreator", creatingUser)); } c.setCacheable(true).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); return c.list(); } @Override public void endLastAllocation(TASK_INSTANCE entityTaskInstance) { entityTaskInstance.setAllocatedUser(null); getSession().saveOrUpdate(entityTaskInstance); List<? extends IEntityTaskInstanceHistory> histories = entityTaskInstance.getTaskHistory(); for (ListIterator<? extends IEntityTaskInstanceHistory> it = histories.listIterator(histories.size()); it .hasPrevious();) { IEntityTaskInstanceHistory history = it.previous(); if (history.getType().getDescription().toLowerCase().contains(TaskInstance.ALOCACAO.toLowerCase())) { history.setEndDateAllocation(new Date()); getSession().saveOrUpdate(history); } } } // ------------------------------------------------------- // Util // ------------------------------------------------------- @Override public void refreshModel(IEntityByCod model) { getSession().refresh(model); } @Override public void flushSession() { getSession().flush(); } @Override public void commitTransaction() { getSession().commitAndContinue(); } private static final boolean isVazio(Object obj) { if (obj == null) { return true; } else if (obj instanceof CharSequence) { return StringUtils.isEmpty(StringUtils.trimToNull(obj.toString())); } else if (obj instanceof Collection<?>) { return ((Collection<?>) obj).isEmpty(); } return false; } }