com.axelor.auth.AuditInterceptor.java Source code

Java tutorial

Introduction

Here is the source code for com.axelor.auth.AuditInterceptor.java

Source

/**
 * Axelor Business Solutions
 *
 * Copyright (C) 2005-2016 Axelor (<http://axelor.com>).
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * 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 com.axelor.auth;

import java.io.Serializable;

import javax.persistence.PersistenceException;

import org.hibernate.EmptyInterceptor;
import org.hibernate.Transaction;
import org.hibernate.type.Type;
import org.joda.time.LocalDateTime;

import com.axelor.auth.db.AuditableModel;
import com.axelor.auth.db.Group;
import com.axelor.auth.db.User;
import com.axelor.db.EntityHelper;
import com.axelor.db.JPA;
import com.axelor.db.JpaSequence;
import com.axelor.db.Model;
import com.axelor.db.mapper.Mapper;
import com.axelor.db.mapper.Property;
import com.axelor.meta.db.MetaSequence;

@SuppressWarnings("serial")
public class AuditInterceptor extends EmptyInterceptor {

    private final ThreadLocal<User> currentUser = new ThreadLocal<User>();
    private final ThreadLocal<AuditTracker> tracker = new ThreadLocal<>();

    private static final String UPDATED_BY = "updatedBy";
    private static final String UPDATED_ON = "updatedOn";
    private static final String CREATED_BY = "createdBy";
    private static final String CREATED_ON = "createdOn";

    private static final String ADMIN_USER = "admin";
    private static final String ADMIN_GROUP = "admins";
    private static final String ADMIN_CHECK_FIELD = "code";

    @Override
    public void afterTransactionBegin(Transaction tx) {
        currentUser.set(AuthUtils.getUser());
        tracker.set(new AuditTracker());
    }

    @Override
    public void afterTransactionCompletion(Transaction tx) {
        tracker.remove();
        currentUser.remove();
    }

    @Override
    public void beforeTransactionCompletion(Transaction tx) {
        tracker.get().onComplete(tx);
    }

    private User getUser() {
        User user = currentUser.get();
        if (user == null) {
            user = AuditableRunner.batchUser.get();
        }
        if (user == null || JPA.em().contains(user)) {
            return user;
        }

        user = AuthUtils.getUser(user.getCode());
        if (user == null) {
            return null;
        }

        currentUser.remove();
        currentUser.set(user);

        return user;
    }

    private boolean canUpdate(Object entity, String field, Object prevValue, Object newValue) {
        if (!(entity instanceof Model) || ((Model) entity).getId() == null) {
            return true;
        }
        if (entity instanceof User || entity instanceof Group) {
            if (!ADMIN_CHECK_FIELD.equals(field)) {
                return true;
            }
            if (entity instanceof User && ADMIN_USER.equals(prevValue) && !ADMIN_USER.equals(newValue)) {
                return false;
            }
            if (entity instanceof Group && ADMIN_GROUP.equals(prevValue) && !ADMIN_GROUP.equals(newValue)) {
                return false;
            }
        }
        return true;
    }

    private boolean canDelete(Object entity) {
        if (entity instanceof User && ADMIN_USER.equals(((User) entity).getCode())) {
            return false;
        }
        if (entity instanceof Group && ADMIN_GROUP.equals(((Group) entity).getCode())) {
            return false;
        }
        return true;
    }

    private boolean updateSequence(Object entity, String[] names, Object[] state) {
        if ((entity instanceof MetaSequence) || !(entity instanceof Model)) {
            return false;
        }

        final Mapper mapper = Mapper.of(EntityHelper.getEntityClass(entity));
        boolean updated = false;

        for (int i = 0; i < names.length; i++) {
            if (state[i] != null) {
                continue;
            }
            final Property property = mapper.getProperty(names[i]);
            if (property != null && property.isSequence()) {
                state[i] = JpaSequence.nextValue(property.getSequenceName());
                updated = true;
            }
        }

        return updated;
    }

    @Override
    public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState,
            String[] propertyNames, Type[] types) {

        if (!(entity instanceof AuditableModel)) {
            return false;
        }

        final User user = this.getUser();
        for (int i = 0; i < propertyNames.length; i++) {
            if (!canUpdate(entity, propertyNames[i], previousState[i], currentState[i])) {
                throw new PersistenceException(String.format("You can't update: %s#%s, values (%s=%s)",
                        entity.getClass().getName(), id, propertyNames[i], currentState[i]));
            }
            if (UPDATED_ON.equals(propertyNames[i])) {
                currentState[i] = new LocalDateTime();
            }
            if (UPDATED_BY.equals(propertyNames[i]) && user != null) {
                currentState[i] = user;
            }
        }

        // change tracking
        if (tracker.get() != null) {
            tracker.get().track(getUser(), (AuditableModel) entity, propertyNames, currentState, previousState);
        }

        return true;
    }

    @Override
    public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {

        boolean changed = updateSequence(entity, propertyNames, state);
        if (!(entity instanceof AuditableModel)) {
            return changed;
        }

        final User user = this.getUser();
        for (int i = 0; i < propertyNames.length; i++) {
            if (state[i] != null) {
                continue;
            }
            if (CREATED_ON.equals(propertyNames[i])) {
                state[i] = new LocalDateTime();
            }
            if (CREATED_BY.equals(propertyNames[i]) && user != null) {
                state[i] = user;
            }
        }

        // change tracking
        if (tracker.get() != null) {
            tracker.get().track(user, (AuditableModel) entity, propertyNames, state, null);
        }

        return true;
    }

    @Override
    public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
        if (!canDelete(entity)) {
            throw new PersistenceException(
                    String.format("You can't delete: %s#%s", entity.getClass().getName(), id));
        }
    }
}