org.betaconceptframework.astroboa.engine.jcr.util.PopulateSimpleCmsProperty.java Source code

Java tutorial

Introduction

Here is the source code for org.betaconceptframework.astroboa.engine.jcr.util.PopulateSimpleCmsProperty.java

Source

/*
 * Copyright (C) 2005-2012 BetaCONCEPT Limited
 *
 * This file is part of Astroboa.
 *
 * Astroboa 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.
 *
 * Astroboa 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 Astroboa.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.betaconceptframework.astroboa.engine.jcr.util;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;

import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.ValueFactory;
import javax.jcr.ValueFormatException;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.betaconceptframework.astroboa.api.model.BinaryChannel;
import org.betaconceptframework.astroboa.api.model.CalendarProperty;
import org.betaconceptframework.astroboa.api.model.CmsRepositoryEntity;
import org.betaconceptframework.astroboa.api.model.ContentObject;
import org.betaconceptframework.astroboa.api.model.SimpleCmsProperty;
import org.betaconceptframework.astroboa.api.model.Taxonomy;
import org.betaconceptframework.astroboa.api.model.Topic;
import org.betaconceptframework.astroboa.api.model.ValueType;
import org.betaconceptframework.astroboa.api.model.definition.BinaryPropertyDefinition;
import org.betaconceptframework.astroboa.api.model.definition.SimpleCmsPropertyDefinition;
import org.betaconceptframework.astroboa.api.model.definition.TopicReferencePropertyDefinition;
import org.betaconceptframework.astroboa.api.model.exception.CmsException;
import org.betaconceptframework.astroboa.context.AstroboaClientContextHolder;
import org.betaconceptframework.astroboa.engine.database.dao.CmsRepositoryEntityAssociationDao;
import org.betaconceptframework.astroboa.engine.jcr.dao.TopicDao;
import org.betaconceptframework.astroboa.model.impl.BinaryChannelImpl;
import org.betaconceptframework.astroboa.model.impl.BinaryPropertyImpl;
import org.betaconceptframework.astroboa.model.impl.ItemQName;
import org.betaconceptframework.astroboa.model.impl.SaveMode;
import org.betaconceptframework.astroboa.model.impl.SimpleCmsPropertyImpl;
import org.betaconceptframework.astroboa.model.impl.TopicReferencePropertyImpl;
import org.betaconceptframework.astroboa.model.impl.item.CmsBuiltInItem;
import org.betaconceptframework.astroboa.model.impl.item.DefinitionReservedName;
import org.betaconceptframework.astroboa.model.impl.item.ItemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 
 * @author Gregory Chomatas (gchomatas@betaconcept.com)
 * @author Savvas Triantafyllou (striantafyllou@betaconcept.com)
 *
 */
public class PopulateSimpleCmsProperty {

    private final Logger logger = LoggerFactory.getLogger(PopulateSimpleCmsProperty.class);

    @Autowired
    private CmsRepositoryEntityAssociationDao cmsRepositoryEntityAssociationDao;
    @Autowired
    private TopicDao topicDao;
    @Autowired
    private BinaryChannelUtils binaryChannelUtils;
    @Autowired
    private TopicUtils topicUtils;

    private SaveMode saveMode;
    private Session session;
    private Node propertyContainerNode;
    private SimpleCmsPropertyDefinition propertyDefinition;
    private SimpleCmsProperty simpleCmsPropertyToBeSaved;

    private boolean propertyHasBeenLoadedAndRemoved;

    private Context context;

    public void setContext(Context context) {
        this.context = context;
    }

    /**
     * @param propertyHasBeenLoadedAndRemoved the propertyHasBeenLoadedAndRemoved to set
     */
    public void setPropertyHasBeenLoadedAndRemoved(boolean propertyHasBeenLoadedAndRemoved) {
        this.propertyHasBeenLoadedAndRemoved = propertyHasBeenLoadedAndRemoved;
    }

    public void setSaveMode(SaveMode saveMode) {
        this.saveMode = saveMode;
    }

    public void setSession(Session session) {
        this.session = session;
    }

    public void setPropertyContainerNode(Node propertyContainerNode) {
        this.propertyContainerNode = propertyContainerNode;
    }

    public void setPropertyDefinition(SimpleCmsPropertyDefinition propertyDefinition) {
        this.propertyDefinition = propertyDefinition;
    }

    public void setCmsPropertyToBeSaved(SimpleCmsProperty cmsPropertyToBeSaved) {
        this.simpleCmsPropertyToBeSaved = cmsPropertyToBeSaved;
    }

    public void populate() throws Exception {

        if (logger.isDebugEnabled()) {
            logger.debug("Populate simple property {}", simpleCmsPropertyToBeSaved.getFullPath());
        }

        initialCheck();

        final ValueType definitionValueType = propertyDefinition.getValueType();

        final ItemQName propertyAsItem = ItemUtils.createSimpleItem(propertyDefinition.getName());
        final ValueFactory valueFactory = session.getValueFactory();

        dispatchPopulate(definitionValueType, propertyAsItem, valueFactory);

    }

    private void checkSimplePropertyWithDefinition() throws Exception {

        if (propertyDefinition == null)
            throw new Exception("Undefined property definition ");

        if (propertyDefinition.getValueType() != simpleCmsPropertyToBeSaved.getValueType())
            throw new CmsException("Found type " + simpleCmsPropertyToBeSaved.getValueType() + ", expected type "
                    + propertyDefinition.getValueType() + " for property "
                    + simpleCmsPropertyToBeSaved.getFullPath());

        if (!propertyDefinition.isMultiple()) {
            //Property is not multiple. Check its values
            switch (propertyDefinition.getValueType()) {
            case Binary:
            case Boolean:
            case ObjectReference:
            case Date:
            case Double:
            case Long:
            case TopicReference:
            case String:
                //If property contains more than one value a MultipleOccurence exception will be thrown
                //Otherwise nothing will happen
                simpleCmsPropertyToBeSaved.getSimpleTypeValue();
                break;
            default:
                throw new CmsException(simpleCmsPropertyToBeSaved.getFullPath() + " invalid simple type "
                        + propertyDefinition.getValueType());

            }
        }
    }

    private void dispatchPopulate(ValueType definitionValueType, ItemQName propertyAsItem,
            ValueFactory valueFactory) throws Exception {

        if (propertyDefinition.isSetDefaultValue() && !propertyHasBeenLoadedAndRemoved) {
            //Set default value if there are no values at all
            if (simpleCmsPropertyToBeSaved.hasNoValues() && propertyDefinition.isMandatory()) {
                simpleCmsPropertyToBeSaved.addSimpleTypeValue(propertyDefinition.getDefaultValue());
            }
        }

        //Check values are valid
        ((SimpleCmsPropertyImpl) simpleCmsPropertyToBeSaved).checkValues();

        switch (definitionValueType) {
        case Date:
            //Special case. Check if provided date is profile.created or profile.created
            if (StringUtils.equals("profile.created", propertyDefinition.getPath())
                    && propertyContainerNode.hasProperty("created")) {
                //Property profile.created already exists and therefore it will not change.
                //Issue a warning if value provided is not the same with the one existed
                Calendar providedCreationDate = ((CalendarProperty) simpleCmsPropertyToBeSaved)
                        .getSimpleTypeValue();
                Calendar existingCreationDate = propertyContainerNode.getProperty("created").getValue().getDate();

                if (providedCreationDate == null || existingCreationDate == null
                        || providedCreationDate.getTimeInMillis() != existingCreationDate.getTimeInMillis()) {
                    logger.warn(
                            "User has provided '{}' as the value of property 'profile.created' but this property already contains value "
                                    + "'{}'. Changing the value of this property is not allowed and therefore provided value will be ignored. In fact"
                                    + " it will be replaced by the persisted value.",
                            (providedCreationDate == null ? "null"
                                    : DateFormatUtils.format(providedCreationDate, "dd/MM/yyy HH:mm") + " "
                                            + providedCreationDate.getTimeInMillis()),
                            (existingCreationDate == null ? "null"
                                    : DateFormatUtils.format(existingCreationDate, "dd/MM/yyy HH:mm") + " "
                                            + existingCreationDate.getTimeInMillis()));
                }
            } else if (StringUtils.equals("profile.modified", propertyDefinition.getPath())
                    && propertyContainerNode.hasProperty("modified")) {
                //Property profile.modified already exists and therefore it will not change.
                //Issue a warning if value provided is not the same with the one existed
                Calendar providedModifiedDate = ((CalendarProperty) simpleCmsPropertyToBeSaved)
                        .getSimpleTypeValue();
                Calendar existingModifiedDate = propertyContainerNode.getProperty("modified").getValue().getDate();

                if (providedModifiedDate == null || existingModifiedDate == null
                        || providedModifiedDate.getTimeInMillis() != existingModifiedDate.getTimeInMillis()) {
                    logger.warn(
                            "User has provided '{}' as the value of property 'profile.modified' but this property already contains value "
                                    + "'{}'. Changing the value of this property is not allowed and therefore provided value will be ignored. Values of this "
                                    + " property are provided by Astroboa only.",
                            (providedModifiedDate == null ? "null"
                                    : DateFormatUtils.format(providedModifiedDate, "dd/MM/yyy HH:mm") + " "
                                            + providedModifiedDate.getTimeInMillis()),
                            (existingModifiedDate == null ? "null"
                                    : DateFormatUtils.format(existingModifiedDate, "dd/MM/yyy HH:mm") + " "
                                            + existingModifiedDate.getTimeInMillis()));
                }
            } else {
                populateSimpleProperty(valueFactory, definitionValueType, propertyAsItem);
            }

            break;
        case Boolean:
        case String:
        case Double:
        case Long:
            populateSimpleProperty(valueFactory, definitionValueType, propertyAsItem);
            break;
        case ObjectReference:
            populatePropertyAsContentObject(propertyAsItem);
            break;
        case TopicReference:
            populatePropertyAsTopic(propertyAsItem);
            break;
        case Binary: {

            //If simple cms property corresponds to an unmanaged binary property then
            //we only care about relativeFileSystemPath. 
            if (propertyDefinition instanceof BinaryPropertyDefinition
                    && ((BinaryPropertyDefinition) propertyDefinition).isBinaryChannelUnmanaged()) {
                populateBinaryChannelAsUnmanaged(valueFactory, definitionValueType, propertyAsItem);
            } else {
                populateBinaryChannel();
            }
        }
        default:
            break;
        }
    }

    private void populateBinaryChannelAsUnmanaged(ValueFactory valueFactory, ValueType definitionValueType,
            ItemQName propertyAsItem) throws RepositoryException {

        List<Object> binaryChannelRelativePaths = new ArrayList<Object>();

        if (propertyDefinition.isMultiple()) {
            List simpleTypeValues = simpleCmsPropertyToBeSaved.getSimpleTypeValues();
            for (Object binaryChannel : simpleTypeValues) {
                if (binaryChannel instanceof BinaryChannel) {

                    if (((BinaryChannel) binaryChannel).contentExists()) {
                        binaryChannelRelativePaths.add(((BinaryChannel) binaryChannel).getRelativeFileSystemPath());
                    } else {
                        throw new CmsException("Could not find content for unmanaged binary channel in path "
                                + ((BinaryChannel) binaryChannel).getRelativeFileSystemPath()
                                + " (path is relative to repository UnmanagedDataStore directory)");
                    }
                } else {
                    throw new CmsException("While saving values of binary property "
                            + simpleCmsPropertyToBeSaved.getFullPath()
                            + " found a value of type other than BinaryChannel. " + binaryChannel.getClass());
                }
            }

            JcrNodeUtils.addMultiValueProperty(propertyContainerNode, propertyAsItem, saveMode,
                    binaryChannelRelativePaths, ValueType.String, valueFactory);
        } else {

            Object binaryChannelRelativePath = null;

            if (simpleCmsPropertyToBeSaved.hasValues()) {

                if (simpleCmsPropertyToBeSaved.getSimpleTypeValue() instanceof BinaryChannel) {

                    if (((BinaryChannel) simpleCmsPropertyToBeSaved.getSimpleTypeValue()).contentExists()) {
                        binaryChannelRelativePath = (((BinaryChannel) simpleCmsPropertyToBeSaved
                                .getSimpleTypeValue()).getRelativeFileSystemPath());
                    } else {
                        throw new CmsException("Could not find content for unmanaged binary channel in path "
                                + ((BinaryChannel) simpleCmsPropertyToBeSaved.getSimpleTypeValue())
                                        .getRelativeFileSystemPath()
                                + " (path is relative to repository UnmanagedDataStore directory)");
                    }
                } else {
                    throw new CmsException(
                            "While saving values of binary property " + simpleCmsPropertyToBeSaved.getFullPath()
                                    + " found a value of type other than BinaryChannel. "
                                    + simpleCmsPropertyToBeSaved.getSimpleTypeValue().getClass());
                }
            }

            JcrNodeUtils.addSimpleProperty(saveMode, propertyContainerNode, propertyAsItem,
                    binaryChannelRelativePath, valueFactory, ValueType.String);
        }

    }

    @SuppressWarnings("unchecked")
    private void populatePropertyAsTopic(ItemQName propertyAsItem) throws RepositoryException {

        List<Topic> topicWithCmsIdentifiers = new ArrayList<Topic>();

        if (simpleCmsPropertyToBeSaved.hasValues()) {

            List<Topic> topics = ((TopicReferencePropertyImpl) simpleCmsPropertyToBeSaved).getValues();

            if (CollectionUtils.isNotEmpty(topics)) {
                List<String> acceptedTaxonomies = ((TopicReferencePropertyDefinition) propertyDefinition)
                        .getAcceptedTaxonomies();

                boolean valuesAreRestrictedToSpecificTaxonomies = CollectionUtils.isNotEmpty(acceptedTaxonomies);

                for (Topic topic : topics) {

                    Node topicNode = retrieveTopicNode(topic);

                    if (topicNode == null) {

                        //Very special case
                        //Provided topic has no id, no name and no localized labels
                        //Ignore it
                        if (StringUtils.isBlank(topic.getId()) && StringUtils.isBlank(topic.getName())
                                && !topic.hasLocalizedLabels()) {
                            continue;
                        }

                        try {
                            topicNode = createAndCacheTopic(topic);
                        } catch (Exception e) {
                            logger.warn("Could not create a new topic with id: " + topic.getId() + " name: "
                                    + topic.getName() + " labels: " + topic.getLocalizedLabels()
                                    + " Topic will not be added as a value to proeprty "
                                    + simpleCmsPropertyToBeSaved.getFullPath(), e);
                            continue;
                        }
                    } else {
                        appendTopicWithIdIfTopicIsNew(topic, topicNode);
                    }

                    assertTopicCanBeRelatedToAContentObject(topic, topicNode);

                    //Check that it is of valid taxonomy
                    if (valuesAreRestrictedToSpecificTaxonomies) {
                        assertTopicTaxonomyIsOneOfTheAccepted(acceptedTaxonomies, topic, topicNode);
                    }

                    topicWithCmsIdentifiers.add(topic);
                }
            }
        }

        EntityAssociationUpdateHelper topicUpdateHelper = createDefaultEntityAssociationUpdateHepler(
                propertyAsItem);
        topicUpdateHelper.setValuesToBeAdded(topicWithCmsIdentifiers);
        topicUpdateHelper.update();

    }

    private void assertTopicTaxonomyIsOneOfTheAccepted(List<String> acceptedTaxonomies, Topic topic, Node topicNode)
            throws RepositoryException {
        //Get taxonomy name of topic
        String taxonomyName = retrieveTaxonomyName(topic, topicNode);

        //Before throwing an exception check to see if topic exists. If it does exist
        //then check its taxonomy

        if (StringUtils.isBlank(taxonomyName)) { //Taxonomy name must not be null
            throw new CmsException("Cannot find the taxonomy of topic " + topic.getName()
                    + ". Cannot add topic because property " + propertyDefinition.getFullPath()
                    + "'s values must belong to the following taxonomies " + acceptedTaxonomies);
        } else if (!acceptedTaxonomies.contains(taxonomyName) && //Taxonomy name must belong to accepted Taxonomies OR
                !Taxonomy.REPOSITORY_USER_FOLKSONOMY_NAME.equals(taxonomyName)) // it belongs to a folksonomy
        {
            throw new CmsException("Topic " + topic.getName() + " belongs to taxonomy " + taxonomyName
                    + " where as property " + propertyDefinition.getFullPath()
                    + "'s values must belong to the following taxonomies " + acceptedTaxonomies);
        }

        logger.debug("Topic {} belongs to taxonomy {} which is one of the accepted taxonies {}",
                new Object[] { topic.getName(), taxonomyName, acceptedTaxonomies });
    }

    private void assertTopicCanBeRelatedToAContentObject(Topic topic, Node topicNode)
            throws RepositoryException, ValueFormatException, PathNotFoundException {
        //If Topic does not allows referrer throw an exception
        //      If property does not exist an ItemNotFoundException is thrown
        if (!topicNode.hasProperty(CmsBuiltInItem.AllowsReferrerContentObjects.getJcrName())
                || !topicNode.getProperty(CmsBuiltInItem.AllowsReferrerContentObjects.getJcrName()).getBoolean()) {
            throw new RepositoryException(
                    "Topic " + topic.getName() + " " + topic.getId() + " does not allow referrers");
        }
    }

    private void appendTopicWithIdIfTopicIsNew(Topic topic, Node topicNode) throws RepositoryException {
        logger.debug("Found jcr node {} for topic {}", topicNode.getPath(), topic.getName());

        if (topic.getId() == null) {
            //Probably an existing topic.
            //Set its id
            topic.setId(context.getCmsRepositoryEntityUtils().getCmsIdentifier(topicNode));
        }
    }

    private Node createAndCacheTopic(Topic topic) throws RepositoryException {

        //It is high likely that taxonomy is not set inside topic's context
        if (topic.getTaxonomy() == null) {
            context.getCmsRepositoryEntityUtils().addDefaultTaxonomyToTopic(topic);
        }

        if (!topic.isAllowsReferrerContentObjects()) {
            //Topic must allow objects to refer to it
            //otherwise this object will not be successfully saved
            topic.setAllowsReferrerContentObjects(true);
        }

        topic = topicDao.saveTopic(topic, context);

        Node topicNode = context.retrieveNodeForTopic(topic.getId());

        if (topicNode == null) {
            throw new RepositoryException(
                    "Could not find or create topic with id " + topic.getId() + " and name " + topic.getName());
        } else {
            logger.debug("Found and cached jcr node {} for topic {}", topicNode.getPath(), topic.getName());
        }

        return topicNode;
    }

    private Node retrieveTopicNode(Topic topic) throws RepositoryException {

        if (topic.getId() != null) {
            return context.retrieveNodeForTopic(topic.getId());
        } else if (topic.getName() != null) {
            return context.retrieveNodeForTopic(topic.getName());
        }

        return null;
    }

    private String retrieveTaxonomyName(Topic topic, Node topicNode) throws RepositoryException {
        if (topic.getTaxonomy() != null && StringUtils.isNotBlank((topic.getTaxonomy().getName()))) {
            return topic.getTaxonomy().getName();
        } else {
            //No taxonomy name found. 
            //Try to find taxonomy from topicNode
            if (topicNode != null) {
                Node topicParent = topicNode.getParent();

                while (topicParent != null && !topicParent.isNodeType(CmsBuiltInItem.TaxonomyRoot.getJcrName())) {
                    if (topicParent.isNodeType(CmsBuiltInItem.Taxonomy.getJcrName())) {
                        if (topicParent.getName().equals(CmsBuiltInItem.Folksonomy.getJcrName())) {
                            return CmsBuiltInItem.Folksonomy.getJcrName()
                                    + context.getCmsRepositoryEntityUtils().getCmsIdentifier(topicParent);
                        } else {
                            return topicParent.getName();
                        }
                    }

                    topicParent = topicParent.getParent();
                }
            }

            return null;
        }
    }

    private void populatePropertyAsContentObject(ItemQName propertyAsItem) throws RepositoryException {

        List<ContentObject> contentObjects = new ArrayList<ContentObject>();
        if (propertyDefinition.isMultiple())
            contentObjects.addAll(simpleCmsPropertyToBeSaved.getSimpleTypeValues());
        else {
            Object contentObject = simpleCmsPropertyToBeSaved.getSimpleTypeValue();
            if (contentObject != null) {
                contentObjects.add((ContentObject) contentObject);
            }
        }

        if (CollectionUtils.isNotEmpty(contentObjects)) {
            for (ContentObject contentObject : contentObjects) {

                Node objectNode = null;

                //Check that the referenced object exists
                logger.debug("Retrieving content object {} {} from repository",
                        ((ContentObject) contentObject).getId(), ((ContentObject) contentObject).getSystemName());

                if (StringUtils.isBlank(((ContentObject) contentObject).getId())) {
                    if (StringUtils.isBlank(((ContentObject) contentObject).getSystemName())) {
                        throw new CmsException(
                                "Object reference does not have an id nor a system name and therefore cannot be saved as a value for property "
                                        + simpleCmsPropertyToBeSaved.getAvailableLocalizedLabel("en")
                                        + ". Save object reference first");
                    } else {
                        //Try to load object reference using its system name
                        objectNode = context.retrieveNodeForObject(((ContentObject) contentObject).getSystemName());

                        if (objectNode != null) {
                            //Set object id although it has not been provided
                            ((ContentObject) contentObject)
                                    .setId(context.getCmsRepositoryEntityUtils().getCmsIdentifier(objectNode));
                        }
                    }
                } else {
                    objectNode = context.retrieveNodeForObject(((ContentObject) contentObject).getId());
                }

                if (objectNode == null) {

                    if (!context.getImportConfiguration().saveMissingObjectReferences()) {
                        throw new CmsException("No content object found in repository with id "
                                + ((ContentObject) contentObject).getId() + " and name "
                                + ((ContentObject) contentObject).getSystemName());
                    } else {
                        //Issue a warning and proceed with the rest
                        logger.warn(
                                "Property {}'s value contains a reference to object {} {} which does not exist in the repository {}. However, the value will be saved because the flag 'saveMissingReferences' in import configuration has been set to true",
                                new Object[] { simpleCmsPropertyToBeSaved.getFullPath(),
                                        ((ContentObject) contentObject).getId(),
                                        ((ContentObject) contentObject).getSystemName(),
                                        AstroboaClientContextHolder.getActiveRepositoryId() });
                    }
                }
            }
        }

        EntityAssociationUpdateHelper contentObjectAssociationUpdateHelper = createDefaultEntityAssociationUpdateHepler(
                propertyAsItem);
        contentObjectAssociationUpdateHelper.setValuesToBeAdded(contentObjects);
        contentObjectAssociationUpdateHelper.update();

    }

    private EntityAssociationUpdateHelper<CmsRepositoryEntity> createDefaultEntityAssociationUpdateHepler(
            ItemQName propertyAsItem) throws RepositoryException {

        //For now only content objects are related to topics.
        //Try to find contentObjectNode
        Node contentObjectNode = JcrNodeUtils.getContentObjectNode(propertyContainerNode);

        EntityAssociationUpdateHelper<CmsRepositoryEntity> contentObjectAssociationUpdateHelper = new EntityAssociationUpdateHelper<CmsRepositoryEntity>(
                session, cmsRepositoryEntityAssociationDao, context);

        contentObjectAssociationUpdateHelper.setReferrerCmsRepositoryEntityNode(contentObjectNode);
        contentObjectAssociationUpdateHelper.setReferrerPropertyContainerNode(propertyContainerNode);
        contentObjectAssociationUpdateHelper.setReferrerPropertyName(propertyAsItem);
        contentObjectAssociationUpdateHelper.setReferrerPropertyNameMultivalue(propertyDefinition.isMultiple());

        return contentObjectAssociationUpdateHelper;
    }

    private void populateSimpleProperty(ValueFactory valueFactory, ValueType definitionValueType,
            ItemQName propertyAsItem) throws RepositoryException {

        if (propertyDefinition.isMultiple())
            JcrNodeUtils.addMultiValueProperty(propertyContainerNode, propertyAsItem, saveMode,
                    simpleCmsPropertyToBeSaved.getSimpleTypeValues(), definitionValueType, valueFactory);
        else
            JcrNodeUtils.addSimpleProperty(saveMode, propertyContainerNode, propertyAsItem,
                    simpleCmsPropertyToBeSaved.getSimpleTypeValue(), valueFactory, definitionValueType);
    }

    private void initialCheck() throws Exception {

        if (simpleCmsPropertyToBeSaved == null || propertyContainerNode == null)
            throw new Exception("Content Object or content object node is null");

        checkSimplePropertyWithDefinition();
    }

    private boolean isReservedName(String propertyName) {
        try {
            if (propertyName == null)
                return false;

            //if not found enum method value of throws an IllegalArgumetException or a NullPointer exception
            DefinitionReservedName.valueOf(propertyName.toLowerCase());

            return true;

        } catch (IllegalArgumentException e) {
            //Name value is not a reserved name
            return false;
        }
    }

    private void populateBinaryChannel() throws Exception {

        //      Collect all existing binary channel node astroboa identifiers for this contentObjectNode under the name of this property
        Map<String, Node> binaryChannelsPerCmsIdentifier = binaryChannelUtils
                .partitionBinaryChannelNodeWithTheSameNamePerId(propertyContainerNode,
                        propertyDefinition.getName());

        List<BinaryChannel> binaryChannels = ((BinaryPropertyImpl) simpleCmsPropertyToBeSaved).getValues();

        if (CollectionUtils.isNotEmpty(binaryChannels)) {
            for (BinaryChannel binaryChannel : binaryChannels) {
                final String binaryChannelName = binaryChannel.getName();

                //Do not process a binary channel with a reserved name
                if (!isReservedName(binaryChannelName)) {

                    //Also check that binary channel has the same name with definition
                    if (StringUtils.isBlank(binaryChannelName)
                            || !binaryChannelName.equals(propertyDefinition.getName()))
                        throw new RepositoryException("Binary channel " + binaryChannelName
                                + " does not have the same name with definition " + propertyDefinition.getName());

                    //Check if binaryChannel has an astroboa identifier
                    String binaryChannelIdentifier = binaryChannel.getId();

                    //Populate BinaryChannel only if Content exists
                    if (binaryChannel.isNewContentLoaded() || shouldGetContentFromExternalLocation(binaryChannel)) {
                        binaryChannelUtils.populateBinaryChannelToNode(binaryChannel, propertyContainerNode,
                                session, saveMode, context, true);
                    }

                    if (binaryChannelIdentifier != null)
                        //All went well. Remove identifier from map
                        binaryChannelsPerCmsIdentifier.remove(binaryChannelIdentifier);
                }
            }

            if (simpleCmsPropertyToBeSaved instanceof BinaryPropertyImpl) {
                ((BinaryPropertyImpl) simpleCmsPropertyToBeSaved)
                        .updateBinaryChannelsWithNecessaryInfoForBuildingURLs(true);
            }
        }

        //      Finally remove all binary channels that remained in the map
        for (Node binaryChannelNode : binaryChannelsPerCmsIdentifier.values()) {
            binaryChannelNode.remove();
        }
    }

    private boolean shouldGetContentFromExternalLocation(BinaryChannel binaryChannel) {

        String externalLocationOfTheContent = ((BinaryChannelImpl) binaryChannel).getExternalLocationOfTheContent();

        boolean externalLocationFound = StringUtils.isNotBlank(externalLocationOfTheContent);

        boolean binaryChannelIsNew = StringUtils.isBlank(binaryChannel.getId());

        String binaryFriendlyUrl = null;
        String binaryPermanentUrl = null;

        if (!binaryChannelIsNew) {
            binaryFriendlyUrl = binaryChannel.buildResourceApiURL(null, null, null, null, null, true, false);
            binaryPermanentUrl = binaryChannel.buildResourceApiURL(null, null, null, null, null, false, false);
        }

        return externalLocationFound
                && (binaryChannelIsNew || (!StringUtils.equals(externalLocationOfTheContent, binaryFriendlyUrl)
                        && !StringUtils.equals(externalLocationOfTheContent, binaryPermanentUrl)));

    }
}