/*
* 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.dao;
import java.util.ArrayList;
import java.util.List;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import org.apache.commons.lang.StringUtils;
import org.betaconceptframework.astroboa.api.model.BetaConceptNamespaceConstants;
import org.betaconceptframework.astroboa.api.model.ValueType;
import org.betaconceptframework.astroboa.api.model.definition.CmsPropertyDefinition;
import org.betaconceptframework.astroboa.api.model.exception.CmsException;
import org.betaconceptframework.astroboa.context.AstroboaClientContextHolder;
import org.betaconceptframework.astroboa.engine.jcr.util.CmsRepositoryEntityUtils;
import org.betaconceptframework.astroboa.engine.jcr.util.JcrNodeUtils;
import org.betaconceptframework.astroboa.model.impl.item.CmsBuiltInItem;
import org.betaconceptframework.astroboa.model.impl.item.JcrNamespaceConstants;
import org.betaconceptframework.astroboa.util.PropertyPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
*
* @author Gregory Chomatas (gchomatas@betaconcept.com)
* @author Savvas Triantafyllou (striantafyllou@betaconcept.com)
*
*/
public class ConsistencyCheckerDao extends JcrDaoSupport{
private final Logger logger = LoggerFactory.getLogger(ConsistencyCheckerDao.class);
@Autowired
private ContentDefinitionDao contentDefinitionDao;
@Autowired
private CmsRepositoryEntityUtils cmsRepositoryEntityUtils;
@Transactional(readOnly=false, rollbackFor = CmsException.class, propagation = Propagation.REQUIRED)
public void performReferentialIntegrityCheck() throws Exception{
try{
logger.info("Checking consistency on repository "+ AstroboaClientContextHolder.getActiveRepositoryId());
Session session = getSession();
Node objectRootNode = JcrNodeUtils.getContentObjectRootNode(session);
visitNode(objectRootNode,getSession(), new ArrayList<String>());
}
catch (Exception e)
{
throw new CmsException(e);
}
}
private void visitNode(Node node, Session session, List<String> alreadyVisitedIds) throws Exception{
NodeIterator nodeIt = node.getNodes();
if (nodeIt != null){
while (nodeIt.hasNext()){
Node childNode = nodeIt.nextNode();
if (childNode.isNodeType(CmsBuiltInItem.StructuredContentObject.getJcrName())){
//Object is considered to be a ROOT complex property
visitComplexProperty(childNode, session, childNode.getProperty(CmsBuiltInItem.SystemName.getJcrName()).getString(), "",alreadyVisitedIds);
}
else if (childNode.hasNodes()){
visitNode(childNode,session,alreadyVisitedIds);
}
}
}
}
private void visitComplexProperty(Node nodeRepresentingComplexProperty, Session session, String objectSystemName, String path, List<String> alreadyVisitedIds) throws Exception {
if (nodeRepresentingComplexProperty != null && nodeRepresentingComplexProperty.hasNodes()){
String propertyPath = PropertyPath.createFullPropertyPath(path, nodeRepresentingComplexProperty.getName());
visitSimpleProperties(nodeRepresentingComplexProperty, session, objectSystemName, propertyPath,alreadyVisitedIds);
NodeIterator nodeIt = nodeRepresentingComplexProperty.getNodes();
if (nodeIt != null){
while (nodeIt.hasNext()){
Node childNode = nodeIt.nextNode();
visitComplexProperty(childNode, session, objectSystemName, propertyPath,alreadyVisitedIds);
}
}
}
}
private void visitSimpleProperties(Node nodeRepresentingComplexProperty, Session session, String objectSystemName, String parentPropertyFullPath, List<String> alreadyVisitedIds) throws Exception {
if (nodeRepresentingComplexProperty != null && nodeRepresentingComplexProperty.hasProperties()){
PropertyIterator properties = nodeRepresentingComplexProperty.getProperties();
if (properties!=null){
while (properties.hasNext()){
Property property = properties.nextProperty();
String propertyName = property.getName();
//References are always of type STRING
if (PropertyType.STRING == property.getType()){
if (StringUtils.equals(CmsBuiltInItem.OwnerCmsIdentifier.getJcrName(), propertyName)){
logger.info("Checking consistency on property "+ property.getPath());
checkOwnerExists(property.getValue().getString(),session, objectSystemName,alreadyVisitedIds);
continue;
}
if (propertyName.startsWith(BetaConceptNamespaceConstants.ASTROBOA_PREFIX+":") ||
propertyName.startsWith(JcrNamespaceConstants.JCR_PREFIX+":") ||
propertyName.startsWith(JcrNamespaceConstants.MIX_PREFIX+":") ||
propertyName.startsWith(JcrNamespaceConstants.NT_PREFIX+":")){
continue;
}
String propertyPath = PropertyPath.createFullPropertyPath(parentPropertyFullPath, property.getName());
CmsPropertyDefinition propertyDefinition = contentDefinitionDao.getCmsPropertyDefinition(propertyPath);
if (propertyDefinition == null){
logger.warn("Could not locate definition for property "+propertyPath);
}
else if (ValueType.ObjectReference == propertyDefinition.getValueType()){
logger.info("Checking consistency on property "+ property.getPath());
if (propertyDefinition.isMultiple()){
checkObjectsExist(property.getValues(), session, propertyPath, objectSystemName, alreadyVisitedIds);
}
else{
checkObjectExist(property.getValue(), session, propertyPath, objectSystemName, alreadyVisitedIds);
}
}
else if (ValueType.TopicReference == propertyDefinition.getValueType()){
logger.info("Checking consistency on property "+ property.getPath());
if (propertyDefinition.isMultiple()){
checkTopicsExist(property.getValues(), session, propertyPath, objectSystemName, alreadyVisitedIds);
}
else{
checkTopicExist(property.getValue(),session, propertyPath, objectSystemName,alreadyVisitedIds);
}
}
}
}
}
}
}
private void checkObjectsExist(Value[] objectReferences, Session session, String propertyPath, String objectSystemName, List<String> alreadyVisitedIds) throws RepositoryException {
if (objectReferences != null){
for (Value objectReference : objectReferences){
checkObjectExist(objectReference,session, propertyPath, objectSystemName,alreadyVisitedIds);
}
}
}
private void checkTopicsExist(Value[] topicReferences, Session session, String propertyPath, String objectSystemName, List<String> alreadyVisitedIds) throws RepositoryException {
if (topicReferences != null){
for (Value topicReference : topicReferences){
checkTopicExist(topicReference,session, propertyPath, objectSystemName, alreadyVisitedIds);
}
}
}
private void checkOwnerExists(String ownerId, Session session, String objectSystemName, List<String> alreadyVisitedIds) throws RepositoryException {
if (alreadyVisitedIds.contains(ownerId)){
return;
}
if (cmsRepositoryEntityUtils.retrieveUniqueNodeForRepositoryUser(session, ownerId) == null){
logger.warn("No Repository User found with id {}. Object {}'s owner contains an invalid repository user reference",
ownerId, objectSystemName);
}
alreadyVisitedIds.add(ownerId);
}
private void checkTopicExist(Value topicReference, Session session, String propertyPath, String objectSystemName, List<String> alreadyVisitedIds) throws RepositoryException {
String topicId = topicReference.getString();
if (alreadyVisitedIds.contains(topicId)){
return;
}
if (cmsRepositoryEntityUtils.retrieveUniqueNodeForTopic(session, topicId) == null){
logger.warn("No topic found with id {}. Property {} of object {} contains an invalid topic reference",
new Object[]{topicId, propertyPath, objectSystemName});
}
alreadyVisitedIds.add(topicId);
}
private void checkObjectExist(Value objectReference, Session session, String propertyPath, String objectSystemName, List<String> alreadyVisitedIds) throws RepositoryException {
String objectId = objectReference.getString();
if (alreadyVisitedIds.contains(objectId)){
return;
}
if (cmsRepositoryEntityUtils.retrieveUniqueNodeForContentObject(session, objectId) == null){
logger.warn("No Object found with id {}. Property {} of object {} contains an invalid object reference",
new Object[]{objectId, propertyPath, objectSystemName});
}
alreadyVisitedIds.add(objectId);
}
}