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

Java tutorial

Introduction

Here is the source code for org.betaconceptframework.astroboa.engine.jcr.util.PopulateContentObject.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.Arrays;
import java.util.HashMap;

import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.ValueFormatException;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.version.VersionException;
import javax.security.auth.Subject;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.betaconceptframework.astroboa.api.model.CmsApiConstants;
import org.betaconceptframework.astroboa.api.model.CmsRepositoryEntity;
import org.betaconceptframework.astroboa.api.model.ContentObject;
import org.betaconceptframework.astroboa.api.model.RepositoryUser;
import org.betaconceptframework.astroboa.api.model.StringProperty;
import org.betaconceptframework.astroboa.api.model.ValueType;
import org.betaconceptframework.astroboa.api.model.definition.ContentObjectTypeDefinition;
import org.betaconceptframework.astroboa.api.model.exception.CmsException;
import org.betaconceptframework.astroboa.api.model.exception.CmsNonUniqueContentObjectSystemNameException;
import org.betaconceptframework.astroboa.api.model.query.criteria.ContentObjectCriteria;
import org.betaconceptframework.astroboa.api.security.RepositoryUserIdPrincipal;
import org.betaconceptframework.astroboa.context.AstroboaClientContextHolder;
import org.betaconceptframework.astroboa.context.SecurityContext;
import org.betaconceptframework.astroboa.engine.database.dao.CmsRepositoryEntityAssociationDao;
import org.betaconceptframework.astroboa.engine.jcr.PrototypeFactory;
import org.betaconceptframework.astroboa.engine.jcr.dao.RepositoryUserDao;
import org.betaconceptframework.astroboa.engine.jcr.query.CmsQueryHandler;
import org.betaconceptframework.astroboa.engine.jcr.query.CmsQueryResult;
import org.betaconceptframework.astroboa.model.factory.CmsCriteriaFactory;
import org.betaconceptframework.astroboa.model.impl.LazyCmsProperty;
import org.betaconceptframework.astroboa.model.impl.SaveMode;
import org.betaconceptframework.astroboa.model.impl.item.CmsBuiltInItem;
import org.betaconceptframework.astroboa.util.CmsConstants;
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 PopulateContentObject {

    @Autowired
    private PrototypeFactory prototypeFactory;
    @Autowired
    private CmsRepositoryEntityAssociationDao cmsRepositoryEntityAssociationDao;
    @Autowired
    private CmsQueryHandler cmsQueryHandler;
    @Autowired
    private RepositoryUserDao repositoryUserDao;

    private Node contentObjectNode;
    private ContentObject contentObject;
    private Session session;
    private SaveMode saveMode;
    private Context context;

    private final Logger logger = LoggerFactory.getLogger(getClass());

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

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

    public void setContentObjectNode(Node contentObjectNode) {
        this.contentObjectNode = contentObjectNode;
    }

    public void setContentObject(ContentObject contentObject) {
        this.contentObject = contentObject;
    }

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

    public void populate(ContentObjectTypeDefinition contentObjectTypeDefinition) {
        try {
            //Check that type is the same, if any
            if (contentObjectNode.hasProperty(CmsBuiltInItem.ContentObjectTypeName.getJcrName())) {
                String contentObjectTypeInJcrNode = contentObjectNode
                        .getProperty(CmsBuiltInItem.ContentObjectTypeName.getJcrName()).getString();
                if (!contentObjectTypeInJcrNode.equals(contentObject.getContentObjectType()))
                    throw new CmsException("Content object's jcr node " + contentObjectNode.getPath()
                            + " is of type " + contentObjectTypeInJcrNode + " where as content object is fo type "
                            + contentObject.getContentObjectType());
            } else {
                //Set content object's type
                contentObjectNode.setProperty(CmsBuiltInItem.ContentObjectTypeName.getJcrName(),
                        contentObjectTypeDefinition.getName());
            }

            populateSystemName();

            //Set content object's owner
            populateOwner();

            //Populate Child Properties
            PopulateComplexCmsProperty contentObjectPropertyContainerPopulateTask = prototypeFactory
                    .newPopulateComplexCmsProperty();
            contentObjectPropertyContainerPopulateTask
                    .setComplexProperty(contentObject.getComplexCmsRootProperty());
            contentObjectPropertyContainerPopulateTask.setComplexPropertyDefinition(
                    contentObject.getComplexCmsRootProperty().getPropertyDefinition());
            contentObjectPropertyContainerPopulateTask.setSaveMode(saveMode);
            contentObjectPropertyContainerPopulateTask.setSession(session);
            contentObjectPropertyContainerPopulateTask.setComplexPropertyNode(contentObjectNode);
            contentObjectPropertyContainerPopulateTask.setContentObjectNodeUUID(contentObjectNode.getIdentifier());
            contentObjectPropertyContainerPopulateTask.setContext(context);
            contentObjectPropertyContainerPopulateTask.populate();

            if (StringUtils.isBlank(
                    ((LazyCmsProperty) contentObject.getComplexCmsRootProperty()).getContentObjectNodeUUID())) {
                ((LazyCmsProperty) contentObject.getComplexCmsRootProperty())
                        .setContentObjectNodeUUID(contentObjectNode.getIdentifier());
            }

            if (StringUtils.isBlank(
                    ((LazyCmsProperty) contentObject.getComplexCmsRootProperty()).getPropertyContainerUUID())) {
                ((LazyCmsProperty) contentObject.getComplexCmsRootProperty())
                        .setPropertyContainerNodeUUID(contentObjectNode.getIdentifier());
            }

        } catch (CmsException e) {
            throw e;
        } catch (Exception e) {
            throw new CmsException(e);
        }
    }

    private void populateSystemName() throws ValueFormatException, VersionException, LockException,
            ConstraintViolationException, RepositoryException {

        //Validate system name first
        String systemName = contentObject.getSystemName();

        //System name is NULL or an empty string 
        if (StringUtils.isBlank(systemName)) {

            // User did not specify a system name. Do not create one if object already has one
            if (systemName == null) {

                //If object already has a system name, retrieve value from the repository and update object
                if (contentObjectNode.hasProperty(CmsBuiltInItem.SystemName.getJcrName())) {
                    //Update object with existing system name and return
                    systemName = contentObjectNode.getProperty(CmsBuiltInItem.SystemName.getJcrName()).getString();

                    //Maybe unnecessary check.
                    if (!context.getCmsRepositoryEntityUtils().isValidSystemName(systemName)) {
                        throw new RepositoryException("Existing Object system name " + systemName
                                + " is not valid. It should match pattern " + CmsConstants.SYSTEM_NAME_REG_EXP);
                    }

                    contentObject.setSystemName(systemName);

                    return;
                }
            }

            //System name is empty. This way user specifies that she wants a new system name to be created 
            //Generate a system name
            //1. Check profile.title property from the object and if none if found, check 
            //if title
            systemName = retrieveContentObjectProfileTitle();

            if (StringUtils.isBlank(systemName)) {
                //No profile.title found. Even though this property is considered
                //mandatory and normally this should never happen
                //system name must not be blank. Therefore we create 
                //a value using path to content object node plus its UUID
                systemName = JcrNodeUtils.getYearMonthDayPathForContentObjectNode(contentObjectNode)
                        + contentObjectNode.getIdentifier();
            }

            //Filter created system name
            //Replace anything that is not valid according to SystemName regular expression
            systemName = context.getCmsRepositoryEntityUtils().fixSystemName(systemName);

            contentObject.setSystemName(systemName);
        }

        if (!context.getCmsRepositoryEntityUtils().isValidSystemName(systemName)) {
            throw new RepositoryException("Content Object system name " + systemName
                    + " is not valid. It should match pattern " + CmsConstants.SYSTEM_NAME_REG_EXP);
        }

        //Finally check that system name is unique across all content objects

        if (foundAtLeastOneMoreContentObjectWithSameSystemName(systemName)) {
            //One or more content objects were found with the same systemName.
            //SystemName will be the same with content object id plus some characters from title
            systemName = context.getCmsRepositoryEntityUtils()
                    .fixSystemName(retrieveContentObjectProfileTitle() + "-" + contentObject.getId());

            //Now run again query with new value
            if (foundAtLeastOneMoreContentObjectWithSameSystemName(systemName)) {
                throw new CmsNonUniqueContentObjectSystemNameException(
                        "Another content object exists with system name " + systemName);
            } else {
                logger.warn(
                        "ContentObject {} was saved with system name {} because provided systemName {} was found in another content object",
                        new Object[] { contentObject.getId(), systemName, contentObject.getSystemName() });

                contentObject.setSystemName(systemName);
            }
        }

        JcrNodeUtils.addSimpleProperty(SaveMode.UPDATE, contentObjectNode, CmsBuiltInItem.SystemName, systemName,
                session.getValueFactory(), ValueType.String);

    }

    private boolean foundAtLeastOneMoreContentObjectWithSameSystemName(String systemName)
            throws RepositoryException {
        /*
         * Criterion equalsIgnoreCaseCriterion = CriterionFactory.equalsCaseInsensitive(CmsBuiltInItem.SystemName.getJcrName(), systemName.toLowerCase());
         * Criterion likeIgnoreCaseCriterion = CriterionFactory.likeCaseInsensitive(CmsBuiltInItem.SystemName.getJcrName(), systemName.toLowerCase());
         * coCriteria.addCriterion(CriterionFactory.or(equalsIgnoreCaseCriterion, likeIgnoreCaseCriterion));
         */
        ContentObjectCriteria coCriteria = CmsCriteriaFactory.newContentObjectCriteria();
        coCriteria.addSystemNameEqualsCriterionIgnoreCase(systemName);

        if (contentObject.getId() != null) {
            coCriteria.addIdNotEqualsCriterion(contentObject.getId());
        }
        //We are only interested in result count
        coCriteria.setOffsetAndLimit(0, 0);

        CmsQueryResult nodesWithSameSystemName = cmsQueryHandler.getNodesFromXPathQuery(session, coCriteria);

        return nodesWithSameSystemName != null && nodesWithSameSystemName.getTotalRowCount() > 0;
    }

    private String retrieveContentObjectProfileTitle()
            throws RepositoryException, ValueFormatException, PathNotFoundException {

        if (contentObject.getComplexCmsRootProperty().isChildPropertyLoaded("profile.title")
                && contentObject.getCmsProperty("profile.title") != null
                && ((StringProperty) contentObject.getCmsProperty("profile.title")).hasValues()) {
            return ((StringProperty) contentObject.getCmsProperty("profile.title")).getSimpleTypeValue();
        } else {
            //Profile title has not been loaded. Check jcr node
            if (contentObjectNode.hasProperty("profile/title")) {
                return contentObjectNode.getProperty("profile/title").getString();
            }
        }

        return "";
    }

    private void populateOwner() throws RepositoryException {

        RepositoryUser owner = contentObject.getOwner();

        if (owner == null) {

            owner = retrieveRepositoryUserForActiveClient();

            //If a new content object is saved then load RepositoryUser which represents
            //the connected user, to be the owner of the object. If no such repository user is found
            //then throw an exception
            if (SaveMode.INSERT == saveMode) {
                contentObject.setOwner(owner);
            }

            //Special case. In case of an update check if existing owner is the same
            //with the user performing the update. If so do not throw an exception
            else {

                String existingOwnerId = null;
                if (contentObjectNode.hasProperty(CmsBuiltInItem.OwnerCmsIdentifier.getJcrName())) {
                    existingOwnerId = contentObjectNode.getProperty(CmsBuiltInItem.OwnerCmsIdentifier.getJcrName())
                            .getString();
                }

                if (StringUtils.equals(owner.getId(), existingOwnerId)) {

                    contentObject.setOwner(owner);

                    //In this case there is nothing else to be done.
                    return;
                } else {

                    if (existingOwnerId == null) {
                        //This should never happen
                        logger.warn(
                                "Object " + context.getCmsRepositoryEntityUtils().nodeIdentity(contentObjectNode)
                                        + " does not have an owner. SYSTEM will be the owner");
                        owner = repositoryUserDao.getSystemRepositoryUser();
                        contentObject.setOwner(owner);
                    } else {

                        //No owner has been provided and active user is not the owner of the object.
                        //However, active user has the permissions to update the object, therefore we allow save
                        //and we set the proper owner instance
                        Node repUsernode = context.retrieveNodeForRepositoryUser(existingOwnerId);
                        owner = repositoryUserDao.renderRepositoryUserFromNode(repUsernode, session, null,
                                new HashMap<String, CmsRepositoryEntity>());
                        contentObject.setOwner(owner);

                        //In this case there is nothing else to be done.
                        return;

                    }

                }
            }
        }

        //First of all we try to locate owner id;

        //Owner must exist in repository
        if (owner.getId() == null) {
            if (StringUtils.equals(CmsApiConstants.SYSTEM_REPOSITORY_USER_EXTRENAL_ID, owner.getExternalId())) {
                //Owner is SYSTEM user. Retrieve property identifier
                RepositoryUser systemUser = repositoryUserDao.getSystemRepositoryUser();

                if (!StringUtils.equals(systemUser.getId(), owner.getId())) {
                    owner = systemUser;
                }
            } else if (StringUtils.isBlank(owner.getExternalId())) {
                throw new RepositoryException("Content Object Owner has no id nor external id");
            } else {
                //Search in cache
                Node userNode = context.getNodeFromCache(owner.getExternalId());

                if (userNode == null) {
                    //try to locate using search criteria
                    RepositoryUser repUser = repositoryUserDao.getRepositoryUser(owner.getExternalId());

                    if (repUser == null) {
                        throw new RepositoryException("Content Object Owner with external id "
                                + owner.getExternalId() + " does not exist");
                    } else {
                        owner = repUser;
                        //Cache node
                        context.retrieveNodeForRepositoryUser(repUser.getId());
                    }
                } else {
                    owner.setId(context.getCmsRepositoryEntityUtils().getCmsIdentifier(userNode));
                }
            }
        }

        String newOwnerCmsIdentifier = owner.getId();

        String existingOwnerId = null;
        if (contentObjectNode.hasProperty(CmsBuiltInItem.OwnerCmsIdentifier.getJcrName())) {
            existingOwnerId = contentObjectNode.getProperty(CmsBuiltInItem.OwnerCmsIdentifier.getJcrName())
                    .getString();
        }

        //Update owner only if value does not exist or it is different that the one provided
        if (!StringUtils.equals(newOwnerCmsIdentifier, existingOwnerId)) {

            //Check that new owner does exist
            if (context.retrieveNodeForRepositoryUser(newOwnerCmsIdentifier) == null) {

                //Special case it may be user SYSTEM. in that case we do not throw any exception
                if (StringUtils.equals(CmsApiConstants.SYSTEM_REPOSITORY_USER_EXTRENAL_ID, owner.getExternalId())) {
                    owner = repositoryUserDao.getSystemRepositoryUser();

                    //if ids match do not continue
                    if (StringUtils.equals(owner.getId(), existingOwnerId)) {
                        return;
                    }
                } else {
                    throw new CmsException("RepositoryUser with id " + newOwnerCmsIdentifier + " and externalId "
                            + owner.getExternalId() + " and label " + owner.getLabel()
                            + " does not exist in repository "
                            + AstroboaClientContextHolder.getActiveRepositoryId());
                }
            }

            EntityAssociationUpdateHelper<RepositoryUser> entityAssociationUpdateHelper = new EntityAssociationUpdateHelper<RepositoryUser>(
                    session, cmsRepositoryEntityAssociationDao, context);

            entityAssociationUpdateHelper.setReferrerCmsRepositoryEntityNode(contentObjectNode);
            entityAssociationUpdateHelper.setReferrerPropertyName(CmsBuiltInItem.OwnerCmsIdentifier);
            entityAssociationUpdateHelper.setReferrerPropertyNameMultivalue(false);
            entityAssociationUpdateHelper.setValuesToBeAdded(Arrays.asList(owner));
            entityAssociationUpdateHelper.update();
        }

    }

    private RepositoryUser retrieveRepositoryUserForActiveClient() throws RepositoryException {

        SecurityContext securityContext = AstroboaClientContextHolder.getActiveSecurityContext();

        String repositoryUserId = null;
        String activeUsername = null;

        if (securityContext != null) {

            Subject subject = securityContext.getSubject();

            if (subject != null
                    && CollectionUtils.isNotEmpty(subject.getPrincipals(RepositoryUserIdPrincipal.class))) {

                repositoryUserId = subject.getPrincipals(RepositoryUserIdPrincipal.class).iterator().next()
                        .getName();

                Node repositoryUserNode = context.retrieveNodeForRepositoryUser(repositoryUserId);

                return repositoryUserDao.renderRepositoryUserFromNode(repositoryUserNode, session, null, null);
            } else {
                //Could not locate repository user id. Try with user name
                activeUsername = securityContext.getIdentity();

                return repositoryUserDao.getRepositoryUser(activeUsername);

            }
        }

        throw new CmsException("Could not locate a RepositoryUser neither with  identifier " + repositoryUserId
                + " nor with externalId " + activeUsername);

    }
}