/*
* Copyright (C) 2011 eXo Platform SAS.
*
* This 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 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.services.jcr.ext.distribution.impl;
import org.exoplatform.services.jcr.core.ExtendedNode;
import org.exoplatform.services.jcr.ext.distribution.DataDistributionType;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import java.util.List;
import java.util.Map;
import javax.jcr.InvalidItemStateException;
import javax.jcr.ItemExistsException;
import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
/**
* @author <a href="mailto:nicolas.filotto@exoplatform.com">Nicolas Filotto</a>
* @version $Id$
*
*/
public abstract class AbstractDataDistributionType implements DataDistributionType
{
private static final Log LOG = ExoLogger.getLogger("exo.jcr.component.ext.AbstractDataDistributionType");
/**
* The default node type to use when we create a new node
*/
private static final String DEFAULT_NODE_TYPE = "nt:unstructured".intern();
/**
* {@inheritDoc}
*/
public Node getDataNode(Node rootNode, String dataId) throws PathNotFoundException, RepositoryException
{
return rootNode.getNode(getRelativePath(dataId));
}
/**
* {@inheritDoc}
*/
public Node getOrCreateDataNode(Node rootNode, String dataId) throws RepositoryException
{
return getOrCreateDataNode(rootNode, dataId, null);
}
/**
* {@inheritDoc}
*/
public Node getOrCreateDataNode(Node rootNode, String dataId, String nodeType) throws RepositoryException
{
return getOrCreateDataNode(rootNode, dataId, nodeType, null);
}
/**
* {@inheritDoc}
*/
public Node getOrCreateDataNode(Node rootNode, String dataId, String nodeType, List<String> mixinTypes)
throws RepositoryException
{
return getOrCreateDataNode(rootNode, dataId, nodeType, mixinTypes, null);
}
/**
* {@inheritDoc}
*/
public Node getOrCreateDataNode(Node rootNode, String dataId, String nodeType, List<String> mixinTypes,
Map<String, String[]> permissions) throws RepositoryException
{
try
{
return getDataNode(rootNode, dataId);
}
catch (PathNotFoundException e)
{
if (LOG.isTraceEnabled())
{
LOG.trace("An exception occurred: " + e.getMessage());
}
}
// The node could not be found so we need to create it
Node node = rootNode;
List<String> ancestors = getAncestors(dataId);
for (int i = 0, length = ancestors.size(); i < length; i++)
{
String nodeName = ancestors.get(i);
try
{
node = node.getNode(nodeName);
continue;
}
catch (PathNotFoundException e)
{
if (LOG.isTraceEnabled())
{
LOG.trace("An exception occurred: " + e.getMessage());
}
}
// The node doesn't exist we need to create it
node = createNode(node, nodeName, nodeType, mixinTypes, permissions, i == length - 1, true);
}
return node;
}
/**
* {@inheritDoc}
*/
public void removeDataNode(Node rootNode, String dataId) throws RepositoryException
{
Node parentNode = null;
try
{
Node node = getDataNode(rootNode, dataId);
parentNode = node.getParent();
node.remove();
parentNode.save();
}
catch (InvalidItemStateException e)
{
if (LOG.isTraceEnabled())
{
LOG.trace("An exception occurred: " + e.getMessage());
}
if (parentNode != null)
parentNode.refresh(false);
}
catch (PathNotFoundException e)
{
if (LOG.isTraceEnabled())
{
LOG.trace("An exception occurred: " + e.getMessage());
}
}
}
/**
* {@inheritDoc}
*/
public void migrate(Node rootNode) throws RepositoryException
{
throw new UnsupportedOperationException("The method is not supported");
}
/**
* {@inheritDoc}
*/
public void migrate(Node rootNode, String nodeType, List<String> mixinTypes, Map<String, String[]> permissions)
throws RepositoryException
{
throw new UnsupportedOperationException("The method is not supported");
}
/**
* Creates the node of the given node type with the given node name directly under
* the given parent node, using the given mixin types and permissions
* @param parentNode the parent node
* @param nodeName the name of the node to create
* @param nodeType the node type to use
* @param mixinTypes the list of mixin types to use
* @param permissions the map of permissions to use
* @param isLeaf indicates whether or not the current node to create is the leaf node
* @return the created node
* @throws RepositoryException if any exception occurs while creating the node
*/
protected Node createNode(final Node parentNode, final String nodeName, final String nodeType,
final List<String> mixinTypes, final Map<String, String[]> permissions, final boolean isLeaf,
final boolean callSave) throws RepositoryException
{
boolean useParameters = !useParametersOnLeafOnly() || (useParametersOnLeafOnly() && isLeaf);
Node node;
if (nodeType == null || nodeType.isEmpty() || !useParameters)
{
node = parentNode.addNode(nodeName, DEFAULT_NODE_TYPE);
}
else
{
node = parentNode.addNode(nodeName, nodeType);
}
if (node.getIndex() > 1)
{
// The node has already been created by a concurrent session
parentNode.refresh(false);
return parentNode.getNode(nodeName);
}
if (useParameters)
{
if (permissions != null && !permissions.isEmpty())
{
if (node.canAddMixin("exo:privilegeable"))
{
node.addMixin("exo:privilegeable");
}
((ExtendedNode)node).setPermissions(permissions);
}
if (mixinTypes != null)
{
for (int i = 0, length = mixinTypes.size(); i < length; i++)
{
String mixin = mixinTypes.get(i);
if (node.canAddMixin(mixin))
{
node.addMixin(mixin);
}
}
}
}
if (callSave)
{
try
{
parentNode.save();
}
catch (ItemExistsException e)
{
if (LOG.isTraceEnabled())
{
LOG.trace("An exception occurred: " + e.getMessage());
}
parentNode.refresh(false);
// Need to check until the concurrent tx that caused this ItemExistsException is fully committed
while (!parentNode.hasNode(nodeName));
return parentNode.getNode(nodeName);
}
}
return node;
}
/**
* Gives the relative path corresponding to the given id of the data to find/create
* @param dataId the id of the data to find/create
* @return the relative path of the data to find/create
*/
protected String getRelativePath(String dataId)
{
StringBuilder buffer = new StringBuilder(256);
List<String> ancestors = getAncestors(dataId);
for (int i = 0, length = ancestors.size(); i < length; i++)
{
buffer.append(ancestors.get(i));
if (i != length - 1)
{
buffer.append('/');
}
}
return buffer.toString();
}
/**
* Gives the list of all the name of the ancestors
* @param dataId the id of the data to find/create
* @return the list of the ancestor names
*/
protected abstract List<String> getAncestors(String dataId);
/**
* Indicates whether or not the node type, the mixin types and the permissions have to
* be used on leaf node only.
* @return <code>true</code> if only the leaf node has to be created with the parameters
* <code>false</code> otherwise.
*/
protected abstract boolean useParametersOnLeafOnly();
}