ch.puzzle.itc.mobiliar.business.auditview.control.AuditService.java Source code

Java tutorial

Introduction

Here is the source code for ch.puzzle.itc.mobiliar.business.auditview.control.AuditService.java

Source

/*
 * AMW - Automated Middleware allows you to manage the configurations of
 * your Java EE applications on an unlimited number of different environments
 * with various versions, including the automated deployment of those apps.
 * Copyright (C) 2013-2016 by Puzzle ITC
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package ch.puzzle.itc.mobiliar.business.auditview.control;

import ch.puzzle.itc.mobiliar.business.auditview.entity.AuditViewEntry;
import ch.puzzle.itc.mobiliar.business.auditview.entity.AuditViewEntryContainer;
import ch.puzzle.itc.mobiliar.business.auditview.entity.Auditable;
import ch.puzzle.itc.mobiliar.business.database.entity.MyRevisionEntity;
import ch.puzzle.itc.mobiliar.business.environment.entity.AbstractContext;
import ch.puzzle.itc.mobiliar.business.environment.entity.HasContexts;
import ch.puzzle.itc.mobiliar.business.resourcegroup.entity.ResourceContextEntity;
import ch.puzzle.itc.mobiliar.business.resourcegroup.entity.ResourceEntity;
import ch.puzzle.itc.mobiliar.business.resourcegroup.entity.ResourceTypeContextEntity;
import ch.puzzle.itc.mobiliar.business.resourcegroup.entity.ResourceTypeEntity;
import ch.puzzle.itc.mobiliar.business.resourcerelation.entity.ConsumedResourceRelationEntity;
import ch.puzzle.itc.mobiliar.business.resourcerelation.entity.ProvidedResourceRelationEntity;
import ch.puzzle.itc.mobiliar.business.resourcerelation.entity.ResourceRelationContextEntity;
import ch.puzzle.itc.mobiliar.business.resourcerelation.entity.ResourceRelationTypeEntity;
import org.apache.commons.lang.StringUtils;
import org.hibernate.envers.AuditReader;
import org.hibernate.envers.AuditReaderFactory;
import org.hibernate.envers.CrossTypeRevisionChangesReader;
import org.hibernate.envers.query.AuditEntity;
import org.hibernate.envers.query.AuditQuery;

import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.*;
import java.util.logging.Logger;

import static org.hibernate.envers.RevisionType.ADD;
import static org.hibernate.envers.RevisionType.DEL;

@Stateless
public class AuditService {

    @Inject
    private Logger log;

    @PersistenceContext
    EntityManager entityManager;

    @Inject
    private AuditHandlerRegistry auditHandlerRegistry;

    @SuppressWarnings("unchecked")
    public <T> Object getDeletedEntity(T entity, Integer id) {
        Objects.requireNonNull(entity, "Entity can not be null");
        Objects.requireNonNull(id, "Id can not be null");

        AuditReader reader = AuditReaderFactory.get(entityManager);

        if (reader.isEntityClassAudited(entity.getClass())) {
            AuditQuery query = reader.createQuery().forRevisionsOfEntity(entity.getClass(), false, true)
                    .add(AuditEntity.id().eq(id)).add(AuditEntity.revisionType().eq(DEL));

            List<Object[]> resultList = query.getResultList();
            if (!resultList.isEmpty()) {
                return resultList.get(0)[0];
            }
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    public List<Object> getAllDeletedEntities(Class clazz) {
        Objects.requireNonNull(clazz, "Clazz can not be null");

        AuditReader reader = AuditReaderFactory.get(entityManager);

        if (reader.isEntityClassAudited(clazz)) {
            AuditQuery query = reader.createQuery().forRevisionsOfEntity(clazz, false, true)
                    .add(AuditEntity.revisionType().eq(DEL));

            List<Object[]> resultList = query.getResultList();
            if (!resultList.isEmpty()) {
                List<Object> deleted = new ArrayList<>();
                for (Object[] objects : resultList) {
                    deleted.add(objects[0]);
                }
                return deleted;
            }
        }
        return Collections.EMPTY_LIST;
    }

    public List<AuditViewEntry> getAuditViewEntriesForResource(Integer resourceId) {
        // Map<Hashcode, AuditViewEntry>
        Map<Integer, AuditViewEntry> allAuditViewEntries = new HashMap<>();
        AuditReader reader = AuditReaderFactory.get(entityManager);
        CrossTypeRevisionChangesReader crossTypeRevisionChangesReader = reader.getCrossTypeRevisionChangesReader();
        List<MyRevisionEntity> revisionsForResource = getRevisionsForResource(resourceId);
        for (MyRevisionEntity revisionEntity : revisionsForResource) {
            List<Object> changedEntitiesForRevision = crossTypeRevisionChangesReader
                    .findEntities(revisionEntity.getId());
            for (Object o : changedEntitiesForRevision) {
                List resultList = reader.createQuery().forRevisionsOfEntity(o.getClass(), false, true)
                        .add(AuditEntity.revisionNumber().eq(revisionEntity.getId())).getResultList();
                createAuditViewEntriesAndAddToMap(allAuditViewEntries, resultList);
            }
        }
        return new ArrayList<>(allAuditViewEntries.values());
    }

    private void createAuditViewEntriesAndAddToMap(Map<Integer, AuditViewEntry> allAuditViewEntries,
            List resultList) {
        for (Object triple : resultList) {
            createSingleAuditViewEntryAndAddToMap(allAuditViewEntries, (Object[]) triple);
        }
    }

    private void createSingleAuditViewEntryAndAddToMap(Map<Integer, AuditViewEntry> allAuditViewEntries,
            Object[] entityRevisionAndRevisionType) {
        Object entity = entityRevisionAndRevisionType[0];
        if (!(entity instanceof Auditable)) {
            log.info(String.format(
                    "The Entity %s does not implement interface Auditable. No Audit log entry will be created for the log view.",
                    entity.getClass().getSimpleName()));
            return;
        }
        AuditViewEntryContainer auditViewEntryContainer = new AuditViewEntryContainer(
                entityRevisionAndRevisionType);

        try {
            AuditHandler handler = auditHandlerRegistry.getAuditHandler(entity.getClass());
            AuditViewEntry auditViewEntry = handler.createAuditViewEntry(auditViewEntryContainer);
            if (isAuditViewEntryRelevant(auditViewEntry, allAuditViewEntries)) {
                allAuditViewEntries.put(auditViewEntry.hashCode(), auditViewEntry);
            }
        } catch (NoAuditHandlerException e) {
            log.info(String.format("No AuditHandler found for %s in AuditHandlerRegistry", entity));
        }
    }

    protected boolean isAuditViewEntryRelevant(AuditViewEntry entry,
            Map<Integer, AuditViewEntry> allAuditViewEntries) {
        if (entry == null) {
            return false;
        }
        if (entry.getMode() == ADD || entry.getMode() == DEL) {
            return true;
        }
        if (allAuditViewEntries.get(entry.hashCode()) != null) {
            return false;
        }
        if (entry.getType().equals(Auditable.TYPE_TEMPLATE_DESCRIPTOR)) {
            // the content of the template descriptors are ignored
            return true;
        }
        if (entry.isObfuscatedValue()) {
            return true;
        }
        return !StringUtils.equals(entry.getOldValue(), entry.getValue());
    }

    private List<MyRevisionEntity> getRevisionsForResource(Integer resourceId) {
        return this.entityManager
                .createQuery("FROM MyRevisionEntity n WHERE n.resourceId = :resourceId", MyRevisionEntity.class)
                .setParameter("resourceId", resourceId).getResultList();
    }

    public void storeIdInThreadLocalForAuditLog(HasContexts<?> hasContexts) {
        if (hasContexts instanceof ResourceTypeEntity) {
            setResourceTypeIdInThreadLocal(hasContexts.getId());
        } else if (hasContexts instanceof ResourceEntity) {
            setResourceIdInThreadLocal(hasContexts.getId());
        } else if (hasContexts instanceof ConsumedResourceRelationEntity) {
            setResourceIdInThreadLocal(((ConsumedResourceRelationEntity) hasContexts).getMasterResourceId());
        } else if (hasContexts instanceof ProvidedResourceRelationEntity) {
            setResourceIdInThreadLocal(((ProvidedResourceRelationEntity) hasContexts).getMasterResource().getId());
        } else if (hasContexts instanceof ResourceRelationTypeEntity) {
            setResourceTypeIdInThreadLocal(((ResourceRelationTypeEntity) hasContexts).getResourceTypeA().getId());
        }
    }

    public void storeIdInThreadLocalForAuditLog(AbstractContext owner) {
        if (owner instanceof ResourceContextEntity) {
            setResourceIdInThreadLocal(((ResourceContextEntity) owner).getResource().getId());
        } else if (owner instanceof ResourceTypeContextEntity) {
            setResourceTypeIdInThreadLocal(((ResourceTypeContextEntity) owner).getResourceTypeEntity().getId());
        } else if (owner instanceof ResourceRelationContextEntity) {
            ConsumedResourceRelationEntity consumed = ((ResourceRelationContextEntity) owner)
                    .getConsumedResourceRelation();
            if (consumed != null) {
                setResourceIdInThreadLocal(consumed.getMasterResourceId());
            }
        }
    }

    public void setResourceTypeIdInThreadLocal(Integer resourceTypeId) {
        ThreadLocalUtil.setThreadVariable(ThreadLocalUtil.KEY_RESOURCE_TYPE_ID, resourceTypeId);
    }

    public void setResourceIdInThreadLocal(Integer resourceId) {
        ThreadLocalUtil.setThreadVariable(ThreadLocalUtil.KEY_RESOURCE_ID, resourceId);
    }

    /**
     * @return a list of three-element arrays, containing:
     * <ol>
     * <li>the entity instance</li>
     * <li>revision entity, corresponding to the revision at which the entity was modified. If no custom
     * revision entity is used, this will be an instance of {@link org.hibernate.envers.DefaultRevisionEntity}</li>
     * <li>type of the revision (an enum instance of class {@link org.hibernate.envers.RevisionType})</li>
     * </ol>
     */
    private <T> List getAllRevisionsForEntity(T entity, Integer id) {
        Objects.requireNonNull(entity, "Entity can not be null");
        Objects.requireNonNull(id, "Id can not be null");

        AuditReader reader = AuditReaderFactory.get(entityManager);
        Number revisionNumberOneYearAgo = getRevisionNumberOneYearAgo(reader);

        if (reader.isEntityClassAudited(entity.getClass())) {
            AuditQuery query = reader.createQuery().forRevisionsOfEntity(entity.getClass(), false, true)
                    .add(AuditEntity.id().eq(id)).add(AuditEntity.revisionNumber().gt(revisionNumberOneYearAgo))
                    .addOrder(AuditEntity.revisionNumber().desc());
            return (List<T>) query.getResultList();
        }
        return null;
    }

    private Number getRevisionNumberOneYearAgo(AuditReader reader) {
        Calendar c = Calendar.getInstance();
        c.setTime(new Date());
        int currentYear = c.get(Calendar.YEAR);
        c.set(Calendar.YEAR, currentYear - 1);
        return reader.getRevisionNumberForDate(c.getTime());
    }

}