Java tutorial
/* * 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); } }