Package org.openengsb.core.ekb.persistence.persist.edb.internal

Source Code of org.openengsb.core.ekb.persistence.persist.edb.internal.EngineeringObjectEnhancer

/**
* Licensed to the Austrian Association for Software Tool Integration (AASTI)
* under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright
* ownership. The AASTI licenses this file to you 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.openengsb.core.ekb.persistence.persist.edb.internal;

import static org.openengsb.core.ekb.persistence.persist.edb.internal.ModelDiff.createModelDiff;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.openengsb.core.api.model.ModelDescription;
import org.openengsb.core.api.model.OpenEngSBModel;
import org.openengsb.core.edb.api.EDBConstants;
import org.openengsb.core.edb.api.EDBException;
import org.openengsb.core.edb.api.EDBObject;
import org.openengsb.core.edb.api.EngineeringDatabaseService;
import org.openengsb.core.ekb.api.EKBCommit;
import org.openengsb.core.ekb.api.EKBException;
import org.openengsb.core.ekb.api.ModelRegistry;
import org.openengsb.core.ekb.api.TransformationEngine;
import org.openengsb.core.ekb.api.hooks.EKBPreCommitHook;
import org.openengsb.core.ekb.common.AdvancedModelWrapper;
import org.openengsb.core.ekb.common.EDBConverter;
import org.openengsb.core.ekb.common.EngineeringObjectModelWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The EngineeringObjectEnhancer enhance an EKBCommit object with additional models which need to be updated or enhance
* inserted models based on the Engineering Object concept of the OpenEngSB.
*/
public class EngineeringObjectEnhancer implements EKBPreCommitHook {
    private static final Logger LOGGER = LoggerFactory.getLogger(EngineeringObjectEnhancer.class);
    // TODO: OPENENGSB-3359, replace edbService and edbConverter with queryInterface
    private EngineeringDatabaseService edbService;
    private EDBConverter edbConverter;
    private TransformationEngine transformationEngine;
    private ModelRegistry modelRegistry;
    private EOMode mode;

    public EngineeringObjectEnhancer(EngineeringDatabaseService edbService, EDBConverter edbConverter,
            TransformationEngine transformationEngine, ModelRegistry modelRegistry, String mode) {
        this.edbService = edbService;
        this.edbConverter = edbConverter;
        this.transformationEngine = transformationEngine;
        this.modelRegistry = modelRegistry;
        try {
            this.mode = EOMode.valueOf(mode);
        } catch (IllegalArgumentException e) {
            this.mode = EOMode.DEACTIVATED;
            LOGGER.error("Unknown mode setting. The engineering object enhancement will be deactivated.", e);
        }
    }

    @Override
    public void onPreCommit(EKBCommit commit) throws EKBException {
        if (mode == EOMode.DEACTIVATED) {
            return;
        }
        enhanceEKBCommit(commit);
    }

    /**
     * Does the Engineering Object enhancement of the EKBCommit object. In this step there may be some inserts updated
     * or new objects will be added to the updates of the EKBCommit object. Throws an EKBException if an error in the
     * Engineering Object logic occurs.
     */
    private void enhanceEKBCommit(EKBCommit commit) throws EKBException {
        LOGGER.debug("Started to enhance the EKBCommit with Engineering Object information");
        enhanceCommitInserts(commit);
        enhanceCommitUpdates(commit);
        // TODO: OPENENGSB-3357, consider also deletions in the enhancement
        LOGGER.debug("Finished EKBCommit enhancing");
    }

    /**
     * Enhances the EKBCommit for the updates of EngineeringObjects.
     */
    private void enhanceCommitUpdates(EKBCommit commit) throws EKBException {
        Map<Object, AdvancedModelWrapper> updated = new HashMap<Object, AdvancedModelWrapper>();
        List<AdvancedModelWrapper> result = recursiveUpdateEnhancement(
            convertOpenEngSBModelList(commit.getUpdates()), updated, commit);
        commit.getUpdates().addAll(convertSimpleModelWrapperList(result));
    }

    /**
     * Converts a list of OpenEngSBModel objects to a list of SimpleModelWrapper objects
     */
    private List<AdvancedModelWrapper> convertOpenEngSBModelList(List<OpenEngSBModel> models) {
        List<AdvancedModelWrapper> wrappers = new ArrayList<AdvancedModelWrapper>();
        for (OpenEngSBModel model : models) {
            wrappers.add(AdvancedModelWrapper.wrap(model));
        }
        return wrappers;
    }

    /**
     * Converts a list of SimpleModelWrapper objects to a list of OpenEngSBModel objects
     */
    private List<OpenEngSBModel> convertSimpleModelWrapperList(List<AdvancedModelWrapper> wrappers) {
        List<OpenEngSBModel> models = new ArrayList<OpenEngSBModel>();
        for (AdvancedModelWrapper wrapper : wrappers) {
            models.add(wrapper.getUnderlyingModel());
        }
        return models;
    }

    /**
     * Recursive function for calculating all models which need to be updated due to the original updates of the
     * EKBCommit.
     */
    private List<AdvancedModelWrapper> recursiveUpdateEnhancement(List<AdvancedModelWrapper> updates,
            Map<Object, AdvancedModelWrapper> updated, EKBCommit commit) {
        List<AdvancedModelWrapper> additionalUpdates = enhanceUpdates(updates, updated, commit);
        for (AdvancedModelWrapper model : updates) {
            updated.put(model.getCompleteModelOID(), model);
        }
        if (!additionalUpdates.isEmpty()) {
            additionalUpdates.addAll(recursiveUpdateEnhancement(additionalUpdates, updated, commit));
        }
        return additionalUpdates;
    }

    /**
     * Enhances the given list of updates and returns a list of models which need to be additionally updated.
     */
    private List<AdvancedModelWrapper> enhanceUpdates(List<AdvancedModelWrapper> updates,
            Map<Object, AdvancedModelWrapper> updated, EKBCommit commit) {
        List<AdvancedModelWrapper> additionalUpdates = new ArrayList<AdvancedModelWrapper>();
        for (AdvancedModelWrapper model : updates) {
            if (updated.containsKey(model.getCompleteModelOID())) {
                continue; // this model was already updated in this commit
            }
            if (model.isEngineeringObject()) {
                additionalUpdates.addAll(performEOModelUpdate(model.toEngineeringObject(), commit));
            }
            additionalUpdates.addAll(getReferenceBasedUpdates(model, updated, commit));
        }
        return additionalUpdates;
    }

    /**
     * Runs the logic of updating an Engineering Object model. Returns a list of models which need to be updated
     * additionally.
     */
    private List<AdvancedModelWrapper> performEOModelUpdate(EngineeringObjectModelWrapper model, EKBCommit commit) {
        ModelDiff diff = createModelDiff(model.getUnderlyingModel(), model.getCompleteModelOID(),
            edbService, edbConverter);
        boolean referencesChanged = diff.isForeignKeyChanged();
        boolean valuesChanged = diff.isValueChanged();
        // TODO: OPENENGSB-3358, Make it possible to change references and values at the same time. Should be
        // no too big deal since we already know at this point which values of the model have been changed and
        // what the old and the new value for the changed properties are
        if (referencesChanged && valuesChanged) {
            throw new EKBException("Engineering Objects may be updated only at "
                    + "references or at values not both in the same commit");
        }
        if (referencesChanged) {
            reloadReferencesAndUpdateEO(diff, model);
        } else {
            return updateReferencedModelsByEO(model);
        }
        return new ArrayList<AdvancedModelWrapper>();
    }

    /**
     * Updates all models which are referenced by the given engineering object.
     */
    private List<AdvancedModelWrapper> updateReferencedModelsByEO(EngineeringObjectModelWrapper model) {
        List<AdvancedModelWrapper> updates = new ArrayList<AdvancedModelWrapper>();
        for (Field field : model.getForeignKeyFields()) {
            try {
                AdvancedModelWrapper result = performMerge(model, loadReferencedModel(model, field));
                if (result != null) {
                    updates.add(result);
                }
            } catch (EDBException e) {
                LOGGER.debug("Skipped referenced model for field {}, since it does not exist.", field, e);
            }
        }
        return updates;
    }

    /**
     * Reload the references which have changed in the actual update and update the Engineering Object accordingly.
     */
    private void reloadReferencesAndUpdateEO(ModelDiff diff, EngineeringObjectModelWrapper model) {
        for (ModelDiffEntry entry : diff.getDifferences().values()) {
            mergeEngineeringObjectWithReferencedModel(entry.getField(), model);
        }
    }

    /**
     * Returns engineering objects to the commit, which are changed by a model which was committed in the EKBCommit
     */
    private List<AdvancedModelWrapper> getReferenceBasedUpdates(AdvancedModelWrapper model,
            Map<Object, AdvancedModelWrapper> updated, EKBCommit commit) throws EKBException {
        List<AdvancedModelWrapper> updates = new ArrayList<AdvancedModelWrapper>();
        List<EDBObject> references = model.getModelsReferringToThisModel(edbService);
        for (EDBObject reference : references) {
            EDBModelObject modelReference = new EDBModelObject(reference, modelRegistry, edbConverter);
            AdvancedModelWrapper ref = updateEOByUpdatedModel(modelReference, model, updated);
            if (!updated.containsKey(ref.getCompleteModelOID())) {
                updates.add(ref);
            }
        }
        return updates;
    }

    /**
     * Updates an Engineering Object given as EDBObject based on the update on the given model which is referenced by
     * the given Engineering Object.
     */
    private AdvancedModelWrapper updateEOByUpdatedModel(EDBModelObject reference, AdvancedModelWrapper model,
            Map<Object, AdvancedModelWrapper> updated) {
        ModelDescription source = model.getModelDescription();
        ModelDescription description = reference.getModelDescription();
        AdvancedModelWrapper wrapper = updated.get(reference.getOID());
        Object ref = null;
        if (wrapper == null) {
            ref = reference.getCorrespondingModel();
        } else {
            ref = wrapper.getUnderlyingModel();
        }
        Object transformResult = transformationEngine.performTransformation(source, description,
            model.getUnderlyingModel(), ref);
        return AdvancedModelWrapper.wrap(transformResult);
    }

    /**
     * Enhances the EKBCommit for the insertion of EngineeringObjects.
     */
    private void enhanceCommitInserts(EKBCommit commit) throws EKBException {
        for (OpenEngSBModel model : commit.getInserts()) {
            AdvancedModelWrapper simple = AdvancedModelWrapper.wrap(model);
            if (simple.isEngineeringObject()) {
                performInsertEOLogic(simple.toEngineeringObject());
            }
        }
    }

    /**
     * Performs the logic for the enhancement needed to be performed to insert an Engineering Object into the EDB.
     */
    private void performInsertEOLogic(EngineeringObjectModelWrapper model) {
        for (Field field : model.getForeignKeyFields()) {
            mergeEngineeringObjectWithReferencedModel(field, model);
        }
    }

    /**
     * Performs the merge from the source model to the target model and returns the result. Returns null if either the
     * source or the target is null.
     */
    private AdvancedModelWrapper performMerge(AdvancedModelWrapper source, AdvancedModelWrapper target) {
        if (source == null || target == null) {
            return null;
        }
        ModelDescription sourceDesc = source.getModelDescription();
        ModelDescription targetDesc = target.getModelDescription();
        Object transformResult = transformationEngine.performTransformation(sourceDesc, targetDesc,
            source.getUnderlyingModel(), target.getUnderlyingModel());
        AdvancedModelWrapper wrapper = AdvancedModelWrapper.wrap(transformResult);
        wrapper.removeOpenEngSBModelEntry(EDBConstants.MODEL_VERSION);
        return wrapper;
    }

    /**
     * Merges the given EngineeringObject with the referenced model which is defined in the given field.
     */
    private void mergeEngineeringObjectWithReferencedModel(Field field, EngineeringObjectModelWrapper model) {
        AdvancedModelWrapper result = performMerge(loadReferencedModel(model, field), model);
        if (result != null) {
            model = result.toEngineeringObject();
        }
    }

    private AdvancedModelWrapper loadReferencedModel(EngineeringObjectModelWrapper eo, Field field) {
        return eo.loadReferencedModel(field, modelRegistry, edbService, edbConverter);
    }
}
TOP

Related Classes of org.openengsb.core.ekb.persistence.persist.edb.internal.EngineeringObjectEnhancer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.