From 9b8fd81bb773e8a8313ebddd0c7233fa7b076184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20J=C3=A4ger?= Date: Thu, 7 Apr 2011 14:06:11 +0200 Subject: [PATCH] Implement JCR-2454 registerNodeTypes via DAVEX This implements registration of node types via DAVEX remoting. The client creates a CND version of the node type definition and passes this on to the server that parses the cnd and registers the node type Also includes the allowUpdate flag in the request. PROPFIND for nodetypes-cnd was implemented. In order to use CompactNodeTypeDefWriter jackrabbit-jcr-server needs to depend on jackrabbit-spi and jackrabbit-spi-commons (not sure if that's a problem). Integration tests for registering node types enabled and running --- .../commons/webdav/JcrRemotingConstants.java | 7 ++- jackrabbit-jcr-server/pom.xml | 10 +++ .../webdav/jcr/ItemResourceConstants.java | 1 + .../webdav/jcr/WorkspaceResourceImpl.java | 82 +++++++++++++++++++- jackrabbit-jcr2dav/pom.xml | 3 - jackrabbit-spi2dav/pom.xml | 3 - .../jackrabbit/spi2dav/RepositoryServiceImpl.java | 51 ++++++++++++- 7 files changed, 144 insertions(+), 13 deletions(-) diff --git a/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/webdav/JcrRemotingConstants.java b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/webdav/JcrRemotingConstants.java index 95ef57d..8c158ae 100644 --- a/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/webdav/JcrRemotingConstants.java +++ b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/webdav/JcrRemotingConstants.java @@ -73,6 +73,10 @@ public interface JcrRemotingConstants { public static final String XML_DESCRIPTORKEY = "descriptorkey"; public static final String XML_DESCRIPTORVALUE = "descriptorvalue"; + // xml elements used for node type registration + public static final String XML_CND = "cnd"; + public static final String XML_ALLOWUPDATE = "allowupdate"; + /** * The 'removeexisting' element is not defined by RFC 3253. If it is present * in the UPDATE request body, uuid conflicts should be solved by removing @@ -126,6 +130,7 @@ public interface JcrRemotingConstants { public static final String JCR_LENGTHS_LN = "lengths"; public static final String JCR_NAMESPACES_LN = "namespaces"; + public static final String JCR_NODETYPES_CND_LN = "nodetypes-cnd"; // property local names used for resource representing a version history public static final String JCR_VERSIONABLEUUID_LN = "versionableuuid"; @@ -143,4 +148,4 @@ public interface JcrRemotingConstants { public static final String REPORT_NODETYPES = "nodetypes"; public static final String REPORT_REGISTERED_NAMESPACES = "registerednamespaces"; public static final String REPORT_REPOSITORY_DESCRIPTORS = "repositorydescriptors"; -} \ No newline at end of file +} diff --git a/jackrabbit-jcr-server/pom.xml b/jackrabbit-jcr-server/pom.xml index 2e55dc0..ba00cf9 100644 --- a/jackrabbit-jcr-server/pom.xml +++ b/jackrabbit-jcr-server/pom.xml @@ -104,6 +104,16 @@ 2.2.6-SNAPSHOT + org.apache.jackrabbit + jackrabbit-spi + 2.2.6-SNAPSHOT + + + org.apache.jackrabbit + jackrabbit-spi-commons + 2.2.6-SNAPSHOT + + org.apache.tika tika-core diff --git a/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/jcr/ItemResourceConstants.java b/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/jcr/ItemResourceConstants.java index 175dcde..d8acbe9 100644 --- a/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/jcr/ItemResourceConstants.java +++ b/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/jcr/ItemResourceConstants.java @@ -84,6 +84,7 @@ public interface ItemResourceConstants extends JcrRemotingConstants { // property names used for resource representing a workspace public static final DavPropertyName JCR_NAMESPACES = DavPropertyName.create(JCR_NAMESPACES_LN, NAMESPACE); + public static final DavPropertyName JCR_NODETYPES_CND = DavPropertyName.create(JCR_NODETYPES_CND_LN, NAMESPACE); // property names used for resource representing a version history public static final DavPropertyName JCR_VERSIONABLEUUID = DavPropertyName.create(JCR_VERSIONABLEUUID_LN, NAMESPACE); diff --git a/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/jcr/WorkspaceResourceImpl.java b/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/jcr/WorkspaceResourceImpl.java index 9b6fa89..80b9c14 100644 --- a/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/jcr/WorkspaceResourceImpl.java +++ b/jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/jcr/WorkspaceResourceImpl.java @@ -18,6 +18,16 @@ package org.apache.jackrabbit.webdav.jcr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.jackrabbit.commons.cnd.CompactNodeTypeDefReader; +import org.apache.jackrabbit.commons.cnd.DefinitionBuilderFactory; +import org.apache.jackrabbit.commons.cnd.ParseException; +import org.apache.jackrabbit.commons.cnd.TemplateBuilderFactory; +import org.apache.jackrabbit.commons.webdav.JcrRemotingConstants; +import org.apache.jackrabbit.spi.QNodeTypeDefinition; +import org.apache.jackrabbit.spi.commons.QNodeTypeDefinitionImpl; +import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver; +import org.apache.jackrabbit.spi.commons.nodetype.compact.CompactNodeTypeDefWriter; +import org.apache.jackrabbit.spi.commons.value.QValueFactoryImpl; import org.apache.jackrabbit.webdav.version.WorkspaceResource; import org.apache.jackrabbit.webdav.version.DeltaVResource; import org.apache.jackrabbit.webdav.version.UpdateInfo; @@ -40,6 +50,7 @@ import org.apache.jackrabbit.webdav.search.SearchResource; import org.apache.jackrabbit.webdav.jcr.property.NamespacesProperty; import org.apache.jackrabbit.webdav.jcr.version.report.JcrPrivilegeReport; import org.apache.jackrabbit.webdav.property.DavProperty; +import org.apache.jackrabbit.webdav.property.DefaultDavProperty; import org.apache.jackrabbit.webdav.property.PropEntry; import org.apache.jackrabbit.webdav.io.InputContext; import org.apache.jackrabbit.webdav.io.OutputContext; @@ -52,7 +63,12 @@ import javax.jcr.Item; import javax.jcr.Session; import javax.jcr.Repository; import javax.jcr.version.Version; +import javax.jcr.nodetype.NodeTypeIterator; +import javax.jcr.nodetype.NodeTypeTemplate; import javax.jcr.observation.EventListener; + +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Date; import java.util.Map; @@ -61,6 +77,8 @@ import java.util.Collections; import java.io.IOException; import java.io.PrintWriter; import java.io.OutputStreamWriter; +import java.io.StringReader; +import java.io.StringWriter; /** * WorkspaceResourceImpl... @@ -239,8 +257,10 @@ public class WorkspaceResourceImpl extends AbstractResource } /** - * Allows to alter the registered namespaces ({@link ItemResourceConstants#JCR_NAMESPACES}) and - * forwards any other property to the super class.

+ * Allows to alter the registered namespaces ({@link ItemResourceConstants#JCR_NAMESPACES}) + * or register node types ({@link ItemResourceConstants#JCR_NODETYPES_CND) + * where the passed value is a cnd string containing the definition + * and forwards any other property to the super class.

* Note that again no property status is set. Any failure while setting * a property results in an exception (violating RFC 2518). * @@ -274,8 +294,45 @@ public class WorkspaceResourceImpl extends AbstractResource } catch (RepositoryException e) { throw new JcrDavException(e); } + } else if (ItemResourceConstants.JCR_NODETYPES_CND.equals(property.getName())) { + try { + Object value = property.getValue(); + if (!(value instanceof List)) { + log.warn("Unexpected structure of dcr:nodetypes-cnd property."); + throw new DavException(DavServletResponse.SC_INTERNAL_SERVER_ERROR); + } + String cnd = null; + boolean allowUpdate = false; + for (Object listEntry : (List) value) { + if (listEntry instanceof Element) { + Element e = (Element)listEntry; + if (JcrRemotingConstants.XML_CND.equals(e.getLocalName())) { + cnd = DomUtil.getText(e); + } else if (JcrRemotingConstants.XML_ALLOWUPDATE.equals(e.getLocalName())) { + String allow = DomUtil.getTextTrim(e); + allowUpdate = Boolean.parseBoolean(allow); + } + } + } + StringReader reader = new StringReader(cnd); + Session session = getRepositorySession(); + Workspace wsp = session.getWorkspace(); + DefinitionBuilderFactory factory = + new TemplateBuilderFactory(wsp.getNodeTypeManager(), session.getValueFactory(), wsp.getNamespaceRegistry()); + + CompactNodeTypeDefReader cndReader = + new CompactNodeTypeDefReader(reader, "davex", factory); + + List ntts = cndReader.getNodeTypeDefinitions(); + wsp.getNodeTypeManager().registerNodeTypes(ntts.toArray(new NodeTypeTemplate[ntts.size()]), allowUpdate); + } catch (ParseException e) { + throw new DavException(DavServletResponse.SC_BAD_REQUEST, e); + } + catch (RepositoryException e) { + throw new JcrDavException(e); + } } else { - // only jcr:namespace can be modified + // only jcr:namespace or node types can be modified throw new DavException(DavServletResponse.SC_CONFLICT); } } @@ -293,7 +350,8 @@ public class WorkspaceResourceImpl extends AbstractResource PropEntry propEntry = changeList.get(0); // only modification of prop is allowed. removal is not possible if (propEntry instanceof DavProperty - && ItemResourceConstants.JCR_NAMESPACES.equals(((DavProperty)propEntry).getName())) { + && (ItemResourceConstants.JCR_NAMESPACES.equals(((DavProperty)propEntry).getName()) + || ItemResourceConstants.JCR_NODETYPES_CND.equals(((DavProperty)propEntry).getName()))) { DavProperty namespaceProp = (DavProperty) propEntry; setProperty(namespaceProp); } else { @@ -453,6 +511,22 @@ public class WorkspaceResourceImpl extends AbstractResource } catch (RepositoryException e) { log.error("Failed to access NamespaceRegistry: " + e.getMessage()); } + try { + NodeTypeIterator ntti = getRepositorySession().getWorkspace().getNodeTypeManager().getAllNodeTypes(); + Collection defs = new ArrayList(); + while (ntti.hasNext()) { + defs.add(new QNodeTypeDefinitionImpl(ntti.nextNodeType(), new DefaultNamePathResolver(getRepositorySession()), QValueFactoryImpl.getInstance())); + } + StringWriter sw = new StringWriter(); + CompactNodeTypeDefWriter writer = new CompactNodeTypeDefWriter(sw, getRepositorySession(), true); + writer.write(defs); + writer.close(); + properties.add(new DefaultDavProperty(ItemResourceConstants.JCR_NODETYPES_CND, sw.toString(), false)); + } catch (RepositoryException e) { + log.error("Failed to access NodeTypeManager: " + e.getMessage()); + } catch (IOException e) { + log.error("Failed to write compact node definition: " + e.getMessage()); + } // TODO: required property DAV:workspace-checkout-set (computed) } diff --git a/jackrabbit-jcr2dav/pom.xml b/jackrabbit-jcr2dav/pom.xml index e66900f..d833a13 100644 --- a/jackrabbit-jcr2dav/pom.xml +++ b/jackrabbit-jcr2dav/pom.xml @@ -64,9 +64,6 @@ org.apache.jackrabbit.test.api.lock.LockManagerTest#testAddLockTokenToAnotherSession org.apache.jackrabbit.test.api.lock.LockManagerTest#testLockTransfer2 org.apache.jackrabbit.jcr2spi.lock.OpenScopedLockTest#testLogoutHasNoEffect - - org.apache.jackrabbit.test.api.nodetype.NodeTypeCreationTest#testRegisterNodeType - org.apache.jackrabbit.test.api.nodetype.NodeTypeCreationTest#testRegisterNodeTypes org.apache.jackrabbit.test.api.observation.GetUserDataTest diff --git a/jackrabbit-spi2dav/pom.xml b/jackrabbit-spi2dav/pom.xml index 1b6b655..ebeaeb3 100644 --- a/jackrabbit-spi2dav/pom.xml +++ b/jackrabbit-spi2dav/pom.xml @@ -61,9 +61,6 @@ org.apache.jackrabbit.test.api.lock.LockManagerTest#testAddInvalidLockToken org.apache.jackrabbit.test.api.lock.LockManagerTest#testAddLockTokenToAnotherSession org.apache.jackrabbit.test.api.lock.LockManagerTest#testLockTransfer2 - - org.apache.jackrabbit.test.api.nodetype.NodeTypeCreationTest#testRegisterNodeType - org.apache.jackrabbit.test.api.nodetype.NodeTypeCreationTest#testRegisterNodeTypes org.apache.jackrabbit.test.api.observation.GetUserDataTest diff --git a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java index 6074779..912dc85 100644 --- a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java +++ b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java @@ -20,8 +20,10 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; +import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -119,6 +121,7 @@ import org.apache.jackrabbit.spi.commons.conversion.PathResolver; import org.apache.jackrabbit.spi.commons.name.NameConstants; import org.apache.jackrabbit.spi.commons.namespace.AbstractNamespaceResolver; import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver; +import org.apache.jackrabbit.spi.commons.nodetype.compact.CompactNodeTypeDefWriter; import org.apache.jackrabbit.spi.commons.value.QValueValue; import org.apache.jackrabbit.spi.commons.value.ValueFactoryQImpl; import org.apache.jackrabbit.spi.commons.value.ValueFormat; @@ -2236,8 +2239,24 @@ public class RepositoryServiceImpl implements RepositoryService, DavConstants { * {@inheritDoc} */ public void registerNodeTypes(SessionInfo sessionInfo, QNodeTypeDefinition[] nodeTypeDefinitions, boolean allowUpdate) throws InvalidNodeTypeDefinitionException, NodeTypeExistsException, UnsupportedRepositoryOperationException, RepositoryException { - // TODO - throw new UnsupportedOperationException("JCR-2003. Implementation missing"); + PropPatchMethod method = null; + try { + DavPropertySet setProperties = new DavPropertySet(); + setProperties.add(createRegisterNodeTypesProperty(sessionInfo, nodeTypeDefinitions, allowUpdate)); + String uri = uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName()); + method = new PropPatchMethod(uri, setProperties, new DavPropertyNameSet()); + initMethod(method, sessionInfo, true); + getClient(sessionInfo).executeMethod(method); + method.checkSuccess(); + } catch (IOException e) { + throw new RepositoryException(e); + } catch (DavException e) { + throw ExceptionConverter.generate(e); + } finally { + if (method != null) { + method.releaseConnection(); + } + } } /** @@ -2334,6 +2353,34 @@ public class RepositoryServiceImpl implements RepositoryService, DavConstants { return new DefaultDavProperty>(JcrRemotingConstants.JCR_NAMESPACES_LN, val, ItemResourceConstants.NAMESPACE, false); } + private DavProperty> createRegisterNodeTypesProperty(SessionInfo sessionInfo, QNodeTypeDefinition[] nodeTypeDefinitions, final boolean allowUpdate) throws IOException { + // convert the specified namespace to a xml-serializable value + List val = new ArrayList(); + + StringWriter sw = new StringWriter(); + CompactNodeTypeDefWriter writer = new CompactNodeTypeDefWriter(sw, new NamespaceResolverImpl(sessionInfo), true); + writer.write(Arrays.asList(nodeTypeDefinitions)); + writer.close(); + + final String cnd = sw.toString(); + + val.add(new XmlSerializable() { + public Element toXml(Document document) { + Element cndElem = document.createElementNS(JcrRemotingConstants.NS_URI, JcrRemotingConstants.NS_PREFIX + ":" + JcrRemotingConstants.XML_CND); + DomUtil.setText(cndElem,cnd); + return cndElem; + } + }); + val.add(new XmlSerializable() { + public Element toXml(Document document) { + Element allowElem = document.createElementNS(JcrRemotingConstants.NS_URI, JcrRemotingConstants.NS_PREFIX + ":" + JcrRemotingConstants.XML_ALLOWUPDATE); + DomUtil.setText(allowElem, Boolean.toString(allowUpdate)); + return allowElem; + } + }); + + return new DefaultDavProperty>(JcrRemotingConstants.JCR_NODETYPES_CND_LN, val, ItemResourceConstants.NAMESPACE, false); + } private static DavProperty> createNodeTypeProperty(String localName, String[] ntNames) { // convert the specified node type names to a xml-serializable value -- 1.7.4