com.openmeap.model.ModelManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.openmeap.model.ModelManagerImpl.java

Source

/*
 ###############################################################################
 #                                                                             #
 #    Copyright (C) 2011-2013 OpenMEAP, Inc.                                   #
 #    Credits to Jonathan Schang & Robert Thacher                              #
 #                                                                             #
 #    Released under the LGPLv3                                                #
 #                                                                             #
 #    OpenMEAP is free software: you can redistribute it and/or modify         #
 #    it under the terms of the GNU Lesser General Public License as published #
 #    by the Free Software Foundation, either version 3 of the License, or     #
 #    (at your option) any later version.                                      #
 #                                                                             #
 #    OpenMEAP 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 Lesser General Public License for more details.                      #
 #                                                                             #
 #    You should have received a copy of the GNU Lesser General Public License #
 #    along with OpenMEAP.  If not, see <http://www.gnu.org/licenses/>.        #
 #                                                                             #
 ###############################################################################
 */

package com.openmeap.model;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.persistence.PersistenceException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import com.openmeap.AuthorizationException;
import com.openmeap.Authorizer;
import com.openmeap.event.EventNotificationException;
import com.openmeap.event.MessagesEvent;
import com.openmeap.event.ProcessingEvent;
import com.openmeap.file.FileOperationException;
import com.openmeap.file.FileOperationManager;
import com.openmeap.model.dto.ClusterNode;
import com.openmeap.model.dto.GlobalSettings;
import com.openmeap.model.event.ModelEntityEvent;
import com.openmeap.model.event.notifier.ModelServiceEventNotifier;
import com.openmeap.model.event.notifier.ModelServiceEventNotifier.CutPoint;

/**
 * Handles all business logic related to the model Entity objects. 
 * 
 * @author schang
 */
public class ModelManagerImpl implements ModelManager, ApplicationContextAware {

    private Collection<ModelServiceEventNotifier> eventNotifiers = null;
    private ModelService modelService;
    private FileOperationManager fileManager;
    private Logger logger = LoggerFactory.getLogger(ModelManagerImpl.class);
    private ApplicationContext context = null;
    private Authorizer authorizer = new Authorizer() {
        @Override
        public Boolean may(Action action, Object object) {
            return Boolean.TRUE;
        }
    };
    private Map<Thread, List<ModelEntityEvent>> eventQueue = new HashMap<Thread, List<ModelEntityEvent>>();

    public ModelManagerImpl() {
    }

    public ModelManagerImpl(ModelService service) {
        setModelService(service);
    }

    @Override
    public ModelManager begin() {
        try {
            fileManager.begin();
        } catch (FileOperationException e) {
            throw new PersistenceException(
                    "An exception was thrown creating a file-resource transaction: " + e.getMessage(), e);
        }
        modelService.begin();
        return this;
    }

    @Override
    public ModelManager commit() throws PersistenceException {
        return commit(null);
    }

    @Override
    public ModelManager commit(List<ProcessingEvent> events) throws PersistenceException {

        processModelEntityEventQueue(CutPoint.IN_COMMIT_BEFORE_COMMIT, events);

        try {
            if (fileManager.isTransactionActive()) {
                fileManager.commit();
            }
        } catch (FileOperationException e) {
            throw new PersistenceException(
                    "An exception was thrown commiting a file-resource transaction: " + e.getMessage(), e);
        }
        modelService.commit();

        processModelEntityEventQueue(CutPoint.IN_COMMIT_AFTER_COMMIT, events);
        clearModelEntityEventQueue();

        return this;
    }

    @Override
    public void rollback() throws PersistenceException {
        clearModelEntityEventQueue();
        try {
            if (fileManager.isTransactionActive()) {
                fileManager.rollback();
            }
        } catch (FileOperationException e) {
            throw new PersistenceException(
                    "An exception was thrown rolling back a file-resource transaction:" + e.getMessage(), e);
        }
        modelService.rollback();
    }

    @Override
    public <T extends ModelEntity> ModelManager refresh(T obj2Refresh, List<ProcessingEvent> events)
            throws PersistenceException {

        ModelEntityEvent event = new ModelEntityEvent(ModelServiceOperation.REFRESH, obj2Refresh);

        stashModelEntityEventTillCommit(event);
        callEventNotifiers(CutPoint.BEFORE_OPERATION, event, events);

        modelService.refresh(obj2Refresh);

        callEventNotifiers(CutPoint.AFTER_OPERATION, event, events);

        return this;
    }

    @Override
    public <T extends ModelEntity> ModelManager delete(T entity, List<ProcessingEvent> events) {

        authorize(entity, Authorizer.Action.DELETE);

        ModelEntityEvent event = new ModelEntityEvent(ModelServiceOperation.DELETE, entity);

        stashModelEntityEventTillCommit(event);
        callEventNotifiers(CutPoint.BEFORE_OPERATION, event, events);

        _delete(entity, events);

        callEventNotifiers(CutPoint.AFTER_OPERATION, event, events);

        return this;
    }

    @Override
    public <T extends ModelEntity> T addModify(T entity, List<ProcessingEvent> events)
            throws InvalidPropertiesException, PersistenceException {

        T revised = entity;

        authorize(entity, determineCreateUpdateAction(entity));

        ModelEntityEvent event = new ModelEntityEvent(ModelServiceOperation.SAVE_OR_UPDATE, entity);

        stashModelEntityEventTillCommit(event);

        callEventNotifiers(CutPoint.BEFORE_OPERATION, event, events);
        revised = (T) event.getPayload();

        revised = (T) _addModify(revised, events);
        event.setPayload(revised);

        callEventNotifiers(CutPoint.AFTER_OPERATION, event, events);
        revised = (T) event.getPayload();

        validate(revised);

        return revised;
    }

    @Override
    public GlobalSettings getGlobalSettings() {
        GlobalSettings settings = modelService.findByPrimaryKey(GlobalSettings.class, (Long) 1L);
        boolean update = false;
        if (settings == null) {
            settings = new GlobalSettings();
            settings.setServiceManagementAuthSalt(UUID.randomUUID().toString());
            update = true;
        }
        if (settings.getServiceManagementAuthSalt() == null
                || settings.getServiceManagementAuthSalt().trim().length() == 0) {
            settings.setServiceManagementAuthSalt(UUID.randomUUID().toString());
            update = true;
        }
        if (update) {
            try {
                modelService.begin();
                settings = modelService.saveOrUpdate(settings);
                modelService.commit();
            } catch (Exception e) {
                modelService.rollback();
                throw new PersistenceException(e);
            }
        }
        return settings;
    }

    @Override
    public ClusterNode getClusterNode() {
        if (context != null) {
            try {
                Map<String, String> servicesWebProperties = (Map<String, String>) context
                        .getBean("openmeapServicesWebPropertiesMap");
                String serviceUrl = null;
                synchronized (servicesWebProperties) {
                    serviceUrl = (String) servicesWebProperties.get("clusterNodeUrlPrefix");
                }
                return this.getGlobalSettings().getClusterNode(serviceUrl);
            } catch (Exception e) {
                logger.warn("{}", e);
            }
        }
        return null;
    }

    /*
     * GETTERS/SETTERS
     */

    public void setEventNotifiers(Collection<ModelServiceEventNotifier> handlers) {
        eventNotifiers = handlers;
    }

    public Collection<ModelServiceEventNotifier> getEventNotifiers() {
        return eventNotifiers;
    }

    public void setAuthorizer(Authorizer auth) {
        this.authorizer = auth;
    }

    public Authorizer getAuthorizer() {
        return authorizer;
    }

    public void setModelService(ModelService service) {
        modelService = service;
    }

    public ModelService getModelService() {
        return modelService;
    }

    public void setFileManager(FileOperationManager fileManager) {
        this.fileManager = fileManager;
    }

    public FileOperationManager getFileManager() {
        return fileManager;
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    /*
     * PRIVATE METHODS
     */

    private <T extends ModelEntity> void _delete(T entity, List<ProcessingEvent> events) {

        if (!getAuthorizer().may(Authorizer.Action.DELETE, entity)) {
            throw new PersistenceException(
                    new AuthorizationException("The user logged in does not have permissions to DELETE "
                            + entity.getClass().getSimpleName() + " objects."));
        }
        modelService.delete(entity);
    }

    private <T extends ModelEntity> T _addModify(T entity, List<ProcessingEvent> events)
            throws InvalidPropertiesException, PersistenceException {

        authorizeAndValidate(entity, determineCreateUpdateAction(entity));
        T o = modelService.saveOrUpdate(entity);
        return o;
    }

    private void stashModelEntityEventTillCommit(ModelEntityEvent event) {
        List<ModelEntityEvent> modelEvents = eventQueue.get(Thread.currentThread());
        if (modelEvents == null) {
            modelEvents = new ArrayList<ModelEntityEvent>();
            eventQueue.put(Thread.currentThread(), modelEvents);
        }
        if (!modelEvents.contains(event)) {
            modelEvents.add(event);
        }
    }

    private void processModelEntityEventQueue(CutPoint cutPoint, List<ProcessingEvent> events) {
        List<ModelEntityEvent> modelEvents;
        if ((modelEvents = eventQueue.get(Thread.currentThread())) != null) {
            int size = modelEvents.size();
            for (int i = 0; i < size; i++) {
                ModelEntityEvent event = modelEvents.get(i);
                callEventNotifiers(cutPoint, event, events);
            }
        }
    }

    private void clearModelEntityEventQueue() {
        List<ModelEntityEvent> modelEvents;
        if ((modelEvents = eventQueue.get(Thread.currentThread())) != null) {
            eventQueue.remove(Thread.currentThread());
        }
    }

    private void callEventNotifiers(CutPoint cutPoint, ModelEntityEvent event, List<ProcessingEvent> events) {

        if (eventNotifiers == null) {
            return;
        }

        for (ModelServiceEventNotifier handler : eventNotifiers) {
            try {

                if (handler.notifiesFor(event.getOperation(), (ModelEntity) event.getPayload())) {
                    switch (cutPoint) {
                    case AFTER_OPERATION:
                        handler.onAfterOperation(event, events);
                        break;
                    case BEFORE_OPERATION:
                        handler.onBeforeOperation(event, events);
                        break;
                    case IN_COMMIT_AFTER_COMMIT:
                        handler.onInCommitAfterCommit(event, events);
                        break;
                    case IN_COMMIT_BEFORE_COMMIT:
                        handler.onInCommitBeforeCommit(event, events);
                        break;
                    }
                }
            } catch (EventNotificationException e) {
                String msg = String.format("EventNotificationException occurred: %s", e.getMessage());
                logger.error(msg);
                if (events != null) {
                    events.add(new MessagesEvent(msg));

                }
            }
        }
    }

    private Authorizer.Action determineCreateUpdateAction(ModelEntity entity) {
        Authorizer.Action action = Authorizer.Action.MODIFY;
        if (entity.getPk() == null) {
            action = Authorizer.Action.CREATE;
        }
        return action;
    }

    private void authorizeAndValidate(ModelEntity entity, Authorizer.Action action)
            throws InvalidPropertiesException {
        authorize(entity, action);
        validate(entity);
    }

    private void authorize(ModelEntity entity, Authorizer.Action action) {
        if (!getAuthorizer().may(action, entity)) {
            throw new PersistenceException(
                    new AuthorizationException("The user logged in does not have permissions to "
                            + action.toString() + " the " + entity.getClass().getSimpleName()));
        }
    }

    private void validate(ModelEntity entity) throws InvalidPropertiesException {
        Map<Method, String> errors = entity.validate();
        if (errors != null) {
            throw new InvalidPropertiesException(entity, errors);
        }
    }
}