/* * 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.activiti.history; import com.google.common.collect.Maps; import org.activiti.engine.history.HistoricVariableUpdate; import org.activiti.engine.impl.TaskQueryImpl; import org.activiti.engine.impl.context.Context; import org.activiti.engine.impl.db.DbSqlSessionFactory; import org.activiti.engine.impl.db.PersistentObject; import org.activiti.engine.impl.identity.Authentication; import org.activiti.engine.impl.persistence.entity.ExecutionEntity; import org.activiti.engine.impl.persistence.entity.HistoricVariableUpdateEntity; import org.activiti.engine.impl.variable.EntityManagerSession; import ru.codeinside.adm.database.AuditId; import ru.codeinside.adm.database.AuditSnapshot; import ru.codeinside.adm.database.AuditValue; import ru.codeinside.gses.activiti.forms.PropertyContext; import ru.codeinside.gses.activiti.forms.Signatures; import ru.codeinside.gses.activiti.jta.CustomDbSqlSession; import ru.codeinside.gses.beans.filevalues.SmevFileValue; import ru.codeinside.gses.webui.data.TaskQueryImpl2; import ru.codeinside.gses.webui.form.FormOvSignatureSeq; import ru.codeinside.gses.webui.form.FormSpSignatureSeq; import ru.codeinside.gses.webui.form.SignatureType; import ru.codeinside.gws.api.Enclosure; import ru.codeinside.gws.api.Signature; import javax.persistence.EntityManager; import java.security.cert.CertificateEncodingException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; final public class HistoricDbSqlSession extends CustomDbSqlSession { /** * Уровень журналирования переменных. */ final private Level varLogLevel = Level.FINE; final private Logger logger = Logger.getLogger(getClass().getName()); final private ArrayList<AuditValue> insertAudits = new ArrayList<AuditValue>(); final private ArrayList<VariableSignature> variableSignatures = new ArrayList<VariableSignature>(); Map<SignatureType, Signatures> currentSignatures; HistoricDbSqlSession(DbSqlSessionFactory dbSqlSessionFactory) { super(dbSqlSessionFactory); } @Override public void insert(final PersistentObject o) { super.insert(o); if (o instanceof HistoricVariableUpdateEntity) { final HistoricVariableUpdateEntity var = (HistoricVariableUpdateEntity) o; final String user = getUser(); logVar(user, var); final String writePath = PropertyContext.getWritePath(); insertAudits.add(new AuditValue(user, Long.parseLong(var.getId()), new VarPath(writePath, var))); // упреждающее инициализируем сесиию, // чтобы не получить конкурентные модификацию на flush getEntityManagerSession(); } } @Override public void flush() { linkPropertyValuesWithSignatures(); super.flush(); } @Override public TaskQueryImpl createTaskQuery() { return new TaskQueryImpl2(); } public void linkPropertyValuesWithSignatures() { if (!insertAudits.isEmpty()) { final EntityManager em = getEntityManagerSession().getEntityManager(); final Map<AuditId, AuditValue> map = Maps.newLinkedHashMap(); for (final AuditValue audit : clearInsertAudits()) { final VarPath varPath = (VarPath) audit.getDetail(); final HistoricVariableUpdate var = varPath.var; if (varPath.path != null && currentSignatures != null) { for (Signatures signatures : currentSignatures.values()) { final int index = signatures.findSign(varPath.path); if (index >= 0 && !((VarPath) audit.getDetail()).var.getVariableName().equals(FormSpSignatureSeq.SIGNED_DATA_ID) && !((VarPath) audit.getDetail()).var.getVariableName().equals(FormOvSignatureSeq.SIGNED_DATA_ID)) { final byte[] sign = signatures.signs[index]; final boolean isAttachment = signatures.files[index]; audit.setSignature(signatures.certificate, sign, isAttachment); } // это вот с какой целью делается? final int spIndex = signatures.findSign(FormSpSignatureSeq.SP_SIGN); if (spIndex >= 0 && ((VarPath) audit.getDetail()).var.getVariableName().equals(FormSpSignatureSeq.SIGNED_DATA_ID)) { final byte[] sign = signatures.signs[spIndex]; final boolean isAttachment = signatures.files[spIndex]; audit.setSignature(signatures.certificate, sign, isAttachment); } // и это final int ovIndex = signatures.findSign(FormOvSignatureSeq.OV_SIGN); if (ovIndex >= 0 && ((VarPath) audit.getDetail()).var.getVariableName().equals(FormOvSignatureSeq.SIGNED_DATA_ID)) { final byte[] sign = signatures.signs[ovIndex]; final boolean isAttachment = signatures.files[ovIndex]; audit.setSignature(signatures.certificate, sign, isAttachment); } } } else { final VariableSignature signature = findVariableSignature(var); if (signature != null) { audit.setSignature(signature.cert, signature.sign, signature.file); } } map.put(new AuditId(getEid(var), var.getVariableName()), audit); em.persist(audit); } for (final AuditId auditId : map.keySet()) { AuditSnapshot snapshot = em.find(AuditSnapshot.class, auditId); if (snapshot != null) { snapshot.setValue(map.get(auditId)); } else { snapshot = new AuditSnapshot(auditId, map.get(auditId)); } em.persist(snapshot); } } variableSignatures.clear(); currentSignatures = null; } private long getEid(final HistoricVariableUpdate h) { return Long.parseLong(getExecutionId(h)); } private String getExecutionId(HistoricVariableUpdate h) { String id = h.getExecutionId(); if (id == null) { id = h.getProcessInstanceId(); } return id; } private List<AuditValue> clearInsertAudits() { final List<AuditValue> audits = new ArrayList<AuditValue>(insertAudits); insertAudits.clear(); return audits; } private EntityManagerSession getEntityManagerSession() { return Context.getCommandContext().getSession(EntityManagerSession.class); } /** * Текущий пользователь из контекста аутентификации. */ private String getUser() { return Authentication.getAuthenticatedUserId(); } private void logVar(String user, HistoricVariableUpdateEntity var) { if (logger.isLoggable(varLogLevel)) { StringBuilder sb = new StringBuilder(); sb.append("var:").append(var.getName()); if (var.getRevision() > 0) { sb.append(", rev:").append(var.getRevision()); } if (user != null) { sb.append(", user:").append(user); } sb.append(", value:").append(var.getValue()); sb.append(", pid:").append(var.getProcessInstanceId()); if (!var.getProcessInstanceId().equals(var.getExecutionId())) { sb.append(", eid:").append(var.getExecutionId()); } if (var.getActivityInstanceId() != null) { sb.append(", aid:").append(var.getActivityInstanceId()); } if (var.getTaskId() != null) { sb.append(", tid:").append(var.getTaskId()); } logger.log(varLogLevel, sb.toString()); } } /** * Метод вызывается в контексте команды Activiti. */ public void addSignatures(Map<SignatureType, Signatures> signatures) { assert currentSignatures == null; assert signatures != null; currentSignatures = signatures; } public void addSignatures(SignatureType type, Signatures signatures) { if (currentSignatures == null) { currentSignatures = new HashMap<SignatureType, Signatures>(); } currentSignatures.put(type, signatures); } public void addSignature(ExecutionId executionId, String varName, byte[] certificate, byte[] sign, boolean isAttachment) { variableSignatures.add(new VariableSignature(executionId, varName, certificate, sign, isAttachment)); } //TODO: что тут является однозначным ключём? private VariableSignature findVariableSignature(HistoricVariableUpdate h) { String variableName = h.getVariableName(); String taskId = h.getTaskId(); if (taskId != null) { for (final VariableSignature v : variableSignatures) { if (variableName.equals(v.variable) && taskId.equals(v.executionId.taskId)) { return v; } } } final String executionId = h.getExecutionId(); if (executionId != null) { for (final VariableSignature v : variableSignatures) { if (variableName.equals(v.variable) && executionId.equals(v.executionId.executionId)) { return v; } } } final String processDefinitionId = cacheGet(ExecutionEntity.class, getExecutionId(h)).getProcessDefinitionId(); for (final VariableSignature v : variableSignatures) { if (variableName.equals(v.variable) && processDefinitionId.equals(v.executionId.processDefinitionId)) { return v; } } if (!variableSignatures.isEmpty()) { logger.log(varLogLevel, "can't find signature for variable " + variableName + " in " + variableSignatures); } return null; } public AuditValue getAuditSnapshotValue(long executionId, String varName) { EntityManager em = getEntityManagerSession().getEntityManager(); AuditSnapshot snapshot = em.find(AuditSnapshot.class, new AuditId(executionId, varName)); if (snapshot != null) { return snapshot.getValue(); } logger.log(varLogLevel, "no history for " + executionId + ":" + varName); return null; } public AuditValue getTempAudit(long eid, String variableName) { for (AuditValue audit : new ArrayList<AuditValue>(insertAudits)) { VarPath varPath = (VarPath) audit.getDetail(); HistoricVariableUpdate h = varPath.var; if (eid == getEid(h) && variableName.equals(h.getVariableName())) { final VariableSignature signature = findVariableSignature(h); if (signature != null) { audit.setSignature(signature.cert, signature.sign, signature.file); } return audit; } } return null; } public boolean addSignaturesBySmevFileValue(String processDefinitionId, String propertyId, SmevFileValue smev) { Enclosure enclosure = smev.getEnclosure(); if (enclosure.signature == null || enclosure.signature.certificate == null) { return false; } Signature signature = enclosure.signature; try { variableSignatures.add(new VariableSignature(new ExecutionId(processDefinitionId), propertyId, signature.certificate.getEncoded(), signature.sign, true)); return true; } catch (CertificateEncodingException e) { e.printStackTrace(); } return false; } }