/* * 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.List; import javax.jcr.ItemNotFoundException; import javax.jcr.Node; import javax.jcr.PathNotFoundException; import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.Value; import javax.jcr.ValueFactory; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.betaconceptframework.astroboa.api.model.ValueType; import org.betaconceptframework.astroboa.api.model.exception.CmsException; import org.betaconceptframework.astroboa.engine.jcr.query.CalendarInfo; import org.betaconceptframework.astroboa.engine.jcr.query.CmsQueryResult; import org.betaconceptframework.astroboa.model.impl.ItemQName; import org.betaconceptframework.astroboa.model.impl.SaveMode; import org.betaconceptframework.astroboa.model.impl.item.CmsBuiltInItem; import org.betaconceptframework.astroboa.model.impl.item.JcrBuiltInItem; import org.betaconceptframework.astroboa.util.CmsConstants; import org.betaconceptframework.astroboa.util.DateUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Gregory Chomatas (gchomatas@betaconcept.com) * @author Savvas Triantafyllou (striantafyllou@betaconcept.com) * */ public class JcrNodeUtils { private final static Logger logger = LoggerFactory.getLogger(JcrNodeUtils.class); /** * Retrieve Content type's Folder Node * Creates a new one if not found * @param session * @param type * @return * @throws RepositoryException * @throws RepositoryException * @throws */ public static Node retrieveOrCreateContentTypeFolderNode(Session session, String type) throws RepositoryException { String typeFolderPath = type + CmsConstants.TYPE_FOLDER; Node contentObjectRootNode = getContentObjectRootNode(session); try { return contentObjectRootNode.getNode(typeFolderPath); } catch (PathNotFoundException pnf) { //Content Type Folder does not exist. Create one return contentObjectRootNode.addNode(typeFolderPath); } } /** * * @param parent * @param childName * @return Child node found with specified childName, null othrewise * @throws RepositoryException */ public static Node selectNode(Node parent, String childName) throws RepositoryException { try{ return parent.getNode(childName); } catch (PathNotFoundException pnfe) { return null; } } public static Node createContentObjectParentNode(Node typeNode, CalendarInfo calendarInfo) throws RepositoryException{ Node secondNode = selectNode(typeNode, calendarInfo.getFullPath()); if (secondNode != null){ return secondNode; } //Obtain year final String year = calendarInfo.getYear(); final String month = calendarInfo.getMonth(); final String day = calendarInfo.getDay(); final String hour = calendarInfo.getHour(); final String minute = calendarInfo.getMinute(); final String second = calendarInfo.getSecond(); Node yearNode = selectNode(typeNode, year); if (yearNode == null){ yearNode = createYearFolderNode(typeNode, year); } Node monthNode = selectNode(yearNode, month); if (monthNode == null){ monthNode = createMonthFolderNode(yearNode, month); } Node dayNode = selectNode(monthNode, day); if (dayNode == null){ dayNode = createDayFolderNode(monthNode, day); } Node hourNode = selectNode(dayNode, hour); if (hourNode == null){ hourNode = createHourFolderNode(dayNode, hour); } Node minuteNode = selectNode(hourNode, minute); if (minuteNode == null){ minuteNode = createMinuteFolderNode(hourNode, minute); } return createSecondFolderNode(minuteNode, second); } private static Node createSecondFolderNode(Node minuteNode, String minute) throws RepositoryException { if (minuteNode == null) throw new ItemNotFoundException("Minute node"); return minuteNode.addNode(minute, CmsBuiltInItem.GenericSecondFolder.getJcrName()); } private static Node createMinuteFolderNode(Node hourNode, String minute) throws RepositoryException { if (hourNode == null) throw new ItemNotFoundException("Hour node"); return hourNode.addNode(minute, CmsBuiltInItem.GenericMinuteFolder.getJcrName()); } private static Node createHourFolderNode(Node dayNode, String hour) throws RepositoryException { if (dayNode == null) throw new ItemNotFoundException("Day node"); return dayNode.addNode(hour, CmsBuiltInItem.GenericHourFolder.getJcrName()); } private static Node createDayFolderNode(Node monthNode, String day) throws RepositoryException { if (monthNode == null) throw new ItemNotFoundException("Month node"); return monthNode.addNode(day, CmsBuiltInItem.GenericDayFolder.getJcrName()); } private static Node createMonthFolderNode(Node yearNode, String month) throws RepositoryException { if (yearNode == null) throw new ItemNotFoundException("Year ndoe"); return yearNode.addNode(month, CmsBuiltInItem.GenericMonthFolder.getJcrName()); } private static Node createYearFolderNode(Node typeNode, String year) throws RepositoryException { if (typeNode == null) throw new ItemNotFoundException("Type node"); return typeNode.addNode(year, CmsBuiltInItem.GenericYearFolder.getJcrName()); } public static Node uniqueNode(CmsQueryResult cmsQueryResult) { if (cmsQueryResult == null) return null; long size = cmsQueryResult.getTotalRowCount(); if (size <= 0) return null; if (size > 1) throw new CmsException("Not unique node"); try { return cmsQueryResult.getNodeIterator().nextNode(); } catch (Exception e) { logger.error("CmsQueryResult has 1 total row count but node iterator has a problem ", e); return null; } } public static Node getRepositoryUserRootNode(Session session) throws RepositoryException { return getRootNode(session, CmsBuiltInItem.RepositoryUserRoot); } public static Node getContentObjectRootNode(Session session) throws RepositoryException { return getRootNode(session, CmsBuiltInItem.ContentObjectRoot); } public static Node getTaxonomyRootNode(Session session) throws RepositoryException { return getRootNode(session, CmsBuiltInItem.TaxonomyRoot); } public static Node getCMSSystemNode(Session session) throws RepositoryException { return session.getRootNode().getNode(CmsBuiltInItem.SYSTEM.getJcrName()); } public static Node getNodeByNativeRepositoryIdentifier(Session session, String nativeRepositoryIdentifier) throws RepositoryException { return session.getNodeByIdentifier(nativeRepositoryIdentifier); } public static Node getRootNode(Session session, ItemQName rootItem) throws RepositoryException { if (rootItem != null) return getCMSSystemNode(session).getNode(rootItem.getJcrName()); return null; } public static void addMultiValueProperty(Node node, ItemQName multiValueProperty, SaveMode saveMode, List values, ValueType valueType, ValueFactory valueFactory) throws RepositoryException { Value[] newValues = JcrValueUtils.convertListToValueArray(values, valueType, valueFactory); addMultiValueProperty(saveMode, node, multiValueProperty, newValues, valueFactory, valueType); } private static void addMultiValueProperty(SaveMode saveMode, Node node, ItemQName property, Value[] values, ValueFactory valueFactory, ValueType valueType) throws RepositoryException { if (! ArrayUtils.isEmpty(values)){ //It may be the case that property contains a single value and not a single value array. //In this case JCR throws an exception. Thus we remove value first and then we add the array of values if (node.hasProperty(property.getJcrName()) && node.getProperty(property.getJcrName()).getDefinition() != null && ! node.getProperty(property.getJcrName()).getDefinition().isMultiple()){ if (values != null){ if (values.length > 1){ throw new CmsException("Cannot save more than one values "+ generateStringOutput(values)+ " to single value property "+ node.getProperty(property.getJcrName()).getPath()+ ".Probably this property used to be single valued and its definition change to multivalue "+ " and now user tries to save more than one values. To correct this propblem "+ " you should run a script which modifies existing property values from single value to a value Array"); } else if (values.length == 1){ addSimpleProperty(saveMode, node, property, JcrValueUtils.getObjectValue(values[0]), valueFactory, valueType); } } else{ node.setProperty(property.getJcrName(), JcrValueUtils.getJcrNull()); } } else{ node.setProperty(property.getJcrName(), values); } } else if (saveMode == SaveMode.UPDATE){ if (node.hasProperty(property.getJcrName()) && node.getProperty(property.getJcrName()).getDefinition() != null && ! node.getProperty(property.getJcrName()).getDefinition().isMultiple()){ removeProperty(node, property, false); } else{ removeProperty(node, property, true); } } } private static String generateStringOutput(Value[] values) { StringBuilder sb = new StringBuilder(); try{ if (! ArrayUtils.isEmpty(values)){ for (Value value : values){ if (value != null){ switch (value.getType()) { case PropertyType.BOOLEAN: sb.append(value.getBoolean()); break; case PropertyType.DATE: sb.append(DateUtils.format(value.getDate())); break; case PropertyType.DOUBLE: sb.append(value.getDouble()); break; case PropertyType.LONG: sb.append(value.getLong()); break; case PropertyType.NAME: case PropertyType.PATH: case PropertyType.REFERENCE: case PropertyType.STRING: sb.append(value.getString()); break; default: break; } } else{ sb.append("NULL"); } sb.append(" "); } } } catch(Exception e){ logger.warn("While trying to log value array", e); } return sb.toString(); } public static void addSimpleProperty(SaveMode saveMode, Node node, ItemQName property, Object value, ValueFactory valueFactory, ValueType valueType) throws RepositoryException { if (value != null){ JcrValueUtils.addValue(node, property, JcrValueUtils.getJcrValue(value, valueType, valueFactory), false); logger.debug("Added value {} for property {} in node {}", new Object[]{value, property.getJcrName(), node.getPath()}); } else if (saveMode == SaveMode.UPDATE){ removeProperty(node, property, false); logger.debug("Removed property {} from node {}", new Object[]{property.getJcrName(), node.getPath()}); } } private static void removeProperty(Node node, ItemQName property, boolean multiValue) throws RepositoryException { if (property != null && node != null) { final String propertyName = property.getJcrName(); if (node.hasProperty(propertyName)) if (multiValue) node.setProperty(propertyName, JcrValueUtils.getJcrNullForMultiValue()); else node.setProperty(propertyName, JcrValueUtils.getJcrNull()); } } public static void addBinaryProperty(SaveMode saveMode, Node node, ItemQName propertyName, byte[] value, ValueFactory valueFactory) throws RepositoryException { if (value != null) { Value binary = JcrValueUtils.getJcrBinary(value, valueFactory); node.setProperty(propertyName.getJcrName(), binary); if (binary !=null){ binary.getBinary().dispose(); } } else if (saveMode == SaveMode.UPDATE){ removeProperty(node, propertyName, false); } } public static Node getTaxonomyJcrNode(Node topicNode, boolean throwExceptionIfNotFound) throws RepositoryException { Node taxonomyNode = topicNode; if (! taxonomyNode.isNodeType(CmsBuiltInItem.Taxonomy.getJcrName())){ //Try to find taxonomy node taxonomyNode = topicNode.getParent(); while (taxonomyNode != null && !taxonomyNode.isNodeType(CmsBuiltInItem.Taxonomy.getJcrName())) taxonomyNode = taxonomyNode.getParent(); } if (throwExceptionIfNotFound && (taxonomyNode == null || !taxonomyNode.isNodeType(CmsBuiltInItem.Taxonomy.getJcrName()))){ throw new ItemNotFoundException("Unable to find taxonomy for topic "+ topicNode.getPath()); } return taxonomyNode; } public static Node getContentObjectNode(Node propertyContainerNode) throws RepositoryException { Node contentObjectNode = propertyContainerNode; if (! contentObjectNode.isNodeType(CmsBuiltInItem.StructuredContentObject.getJcrName())) { //Try to find contentObjectNode contentObjectNode = propertyContainerNode.getParent(); while (!contentObjectNode.isNodeType(CmsBuiltInItem.StructuredContentObject.getJcrName())) contentObjectNode = contentObjectNode.getParent(); } return contentObjectNode; } public static Node getOrganizationSpaceNode(Session session) throws RepositoryException { return getRootNode(session, CmsBuiltInItem.OrganizationSpace); } public static Node addContentObjectNode(Node dayNode, String type) throws RepositoryException { //Create node return dayNode.addNode(type, CmsBuiltInItem.StructuredContentObject.getJcrName()); } public static Node addRepositoryUserNode(Session session) throws RepositoryException { Node repositoryUserNode = getRepositoryUserRootNode(session).addNode(CmsBuiltInItem.RepositoryUser.getJcrName(), CmsBuiltInItem.RepositoryUser.getJcrName()); repositoryUserNode.addNode(CmsBuiltInItem.Folksonomy.getJcrName(), CmsBuiltInItem.Taxonomy.getJcrName()); return repositoryUserNode; } public static Node addSpaceNode(Node parentNode, String nodeNeme) throws RepositoryException { return parentNode.addNode(nodeNeme, CmsBuiltInItem.Space.getJcrName()); } public static Node addTopicNode(Node parentNode, String nodeNeme) throws RepositoryException { return parentNode.addNode(nodeNeme, CmsBuiltInItem.Topic.getJcrName()); } public static Node addBinaryChannelNode(Node binaryParentNode, String name) throws RepositoryException { return binaryParentNode.addNode(name, CmsBuiltInItem.BinaryChannel.getJcrName()); } public static Node addNodeForComplexCmsProperty(Node parentComplexPropertyNode, String name) throws RepositoryException{ Node childComplexPropertyNode = parentComplexPropertyNode.addNode(name, JcrBuiltInItem.NtUnstructured.getJcrName()); childComplexPropertyNode.addMixin(JcrBuiltInItem.MixReferenceable.getJcrName()); return childComplexPropertyNode; } public static Node addLocalizationNode(Node cmsRepositoryEntityNode) throws RepositoryException { return cmsRepositoryEntityNode.addNode(CmsBuiltInItem.Localization.getJcrName(), JcrBuiltInItem.NtUnstructured.getJcrName()); } public static String getYearMonthDayPathForContentObjectNode(Node contentObjectNode) throws RepositoryException{ if (contentObjectNode == null || contentObjectNode.getParent() == null){ return ""; } //In releases prior to 3 (2.x.x) content object nodes were stored //under path contentTypeFolder/year/month/day //where as from release 3 and onwards content object nodes are stored under path //contentTypeFolder/year/month/day/hour/minute //This method takes under consideration both structures for compatibility reasons //Find content object type folder Node contentObjectTypeFolderNode = contentObjectNode.getParent(); //Try to find contentObjectTypeFolder node while (contentObjectTypeFolderNode != null && ! contentObjectTypeFolderNode.isNodeType(CmsBuiltInItem.GenericContentTypeFolder.getJcrName())){ contentObjectTypeFolderNode = contentObjectTypeFolderNode.getParent(); } if (contentObjectTypeFolderNode == null){ return ""; } String contentObjectNodeParentPath = contentObjectNode.getParent().getPath(); String contentObjectTypeFolderPath = contentObjectTypeFolderNode.getPath(); String path = StringUtils.difference(contentObjectTypeFolderPath+"/", contentObjectNodeParentPath); logger.debug("CO Parent Path {} \n Type Folder Path {}\n Difference {}", new Object[]{contentObjectNodeParentPath, contentObjectTypeFolderPath, path}); //Path has either the format year/month/day or year/month/day/hour/minute int count = StringUtils.countMatches(path, "/"); if (count == 2){ return path; } else{ //Must return the first two path = StringUtils.substringBeforeLast(path, "/"); return StringUtils.substringBeforeLast(path, "/"); } } }