diff --git a/config/alfresco/application-context.xml b/config/alfresco/application-context.xml index 25df276103..5cb650346c 100644 --- a/config/alfresco/application-context.xml +++ b/config/alfresco/application-context.xml @@ -23,6 +23,7 @@ + diff --git a/config/alfresco/jcr-api-context.xml b/config/alfresco/jcr-api-context.xml new file mode 100644 index 0000000000..2b12957e2d --- /dev/null +++ b/config/alfresco/jcr-api-context.xml @@ -0,0 +1,20 @@ + + + + + + + + + SpacesStore + + + + + + alfresco/model/jcrModel.xml + + + + + diff --git a/config/alfresco/jcr-context.xml b/config/alfresco/jcr-context.xml new file mode 100644 index 0000000000..b95c728d2d --- /dev/null +++ b/config/alfresco/jcr-context.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/config/alfresco/model/jcrModel.xml b/config/alfresco/model/jcrModel.xml new file mode 100644 index 0000000000..686d587f6f --- /dev/null +++ b/config/alfresco/model/jcrModel.xml @@ -0,0 +1,62 @@ + + + JCR Model Definitions + 1.0 + + + + + + + + + + + + + + + + + + + d:qname + true + true + + + d:qname + true + true + + + + + + + + + + + + d:text + true + true + + + + + + + + d:text + + + d:boolean + + + + + + + \ No newline at end of file diff --git a/project-build.xml b/project-build.xml index 384778a65f..0e8964351e 100644 --- a/project-build.xml +++ b/project-build.xml @@ -21,9 +21,9 @@ author="true" version="true" doctitle="Alfresco Content Management Service API Specification" windowtitle="Alfresco Content Management Service API" classpathref="classpath.compile"> ${javadoc.copyright} - + - + @@ -51,5 +51,37 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/project.properties b/project.properties index f27368e16c..937e555f8c 100644 --- a/project.properties +++ b/project.properties @@ -1,2 +1,6 @@ file.jibx.binding=${dir.src.java}/org/alfresco/repo/dictionary/m2binding.xml dir.javadoc.api.service=${dir.docs}/java-public-service-api + +tck.webinf.lib.excludes=${webinf.lib.excludes},jcr-1.0.jar +tck.file.name.war=alfresco-jcr-tck.war +tck.dir.deploy=/jcr-tck-1.0 \ No newline at end of file diff --git a/source/java/org/alfresco/jcr/api/JCRNodeRef.java b/source/java/org/alfresco/jcr/api/JCRNodeRef.java new file mode 100644 index 0000000000..c7dff4055b --- /dev/null +++ b/source/java/org/alfresco/jcr/api/JCRNodeRef.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.api; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.util.ParameterCheck; + + +/** + * Helper to retrieve an Alfresco Node Reference from a JCR Node + * + * @author David Caruana + */ +public class JCRNodeRef +{ + + /** + * Gets the Node Reference for the specified Node + * + * @param node JCR Node + * @return Alfresco Node Reference + * @throws RepositoryException + */ + public static NodeRef getNodeRef(Node node) + throws RepositoryException + { + ParameterCheck.mandatory("Node", node); + + Property protocol = node.getProperty(NamespaceService.SYSTEM_MODEL_PREFIX + ":" + ContentModel.PROP_STORE_PROTOCOL.getLocalName()); + Property identifier = node.getProperty(NamespaceService.SYSTEM_MODEL_PREFIX + ":" + ContentModel.PROP_STORE_IDENTIFIER.getLocalName()); + Property uuid = node.getProperty(NamespaceService.SYSTEM_MODEL_PREFIX + ":" + ContentModel.PROP_NODE_UUID.getLocalName()); + + return new NodeRef(new StoreRef(protocol.getString(), identifier.getString()), uuid.getString()); + } + + +} diff --git a/source/java/org/alfresco/jcr/dictionary/ClassMap.java b/source/java/org/alfresco/jcr/dictionary/ClassMap.java new file mode 100644 index 0000000000..2f1da30e72 --- /dev/null +++ b/source/java/org/alfresco/jcr/dictionary/ClassMap.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.dictionary; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import javax.jcr.RepositoryException; + +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + + +/** + * Responsible for mapping Alfresco Classes to JCR Types / Mixins and vice versa. + * + * @author David Caruana + */ +public class ClassMap +{ + /** Map of Alfresco Class to JCR Class */ + private static Map JCRToAlfresco = new HashMap(); + static + { + JCRToAlfresco.put(NodeTypeImpl.MIX_REFERENCEABLE, ContentModel.ASPECT_REFERENCEABLE); + JCRToAlfresco.put(NodeTypeImpl.MIX_LOCKABLE, ContentModel.ASPECT_LOCKABLE); + JCRToAlfresco.put(NodeTypeImpl.MIX_VERSIONABLE, ContentModel.ASPECT_VERSIONABLE); + } + + /** Map of JCR Class to Alfresco Class */ + private static Map AlfrescoToJCR = new HashMap(); + static + { + AlfrescoToJCR.put(ContentModel.ASPECT_REFERENCEABLE, NodeTypeImpl.MIX_REFERENCEABLE); + AlfrescoToJCR.put(ContentModel.ASPECT_LOCKABLE, NodeTypeImpl.MIX_LOCKABLE); + AlfrescoToJCR.put(ContentModel.ASPECT_VERSIONABLE, NodeTypeImpl.MIX_VERSIONABLE); + } + + /** Map of JCR to Alfresco "Add Aspect" Behaviours */ + private static Map addMixin = new HashMap(); + static + { + addMixin.put(ContentModel.ASPECT_VERSIONABLE, new VersionableMixin()); + } + + /** Map of JCR to Alfresco "Remove Aspect" Behaviours */ + private static Map removeMixin = new HashMap(); + static + { + removeMixin.put(ContentModel.ASPECT_VERSIONABLE, new VersionableMixin()); + } + + + /** + * Convert an Alfresco Class to a JCR Type + * + * @param jcrType JCR Type + * @return Alfresco Class + * @throws RepositoryException + */ + public static QName convertTypeToClass(QName jcrType) + { + return JCRToAlfresco.get(jcrType); + } + + /** + * Convert an Alfresco Class to a JCR Type + * + * @param alfrescoClass Alfresco Class + * @return JCR Type + * @throws RepositoryException + */ + public static QName convertClassToType(QName alfrescoClass) + { + return JCRToAlfresco.get(alfrescoClass); + } + + /** + * Get 'Add Mixin' JCR behaviour + * + * @param alfrescoClass + * @return AddMixin behaviour + */ + public static AddMixin getAddMixin(QName alfrescoClass) + { + return addMixin.get(alfrescoClass); + } + + /** + * Get 'Remove Mixin' JCR behaviour + * + * @param alfrescoClass + * @return RemoveMixin behaviour + */ + public static RemoveMixin getRemoveMixin(QName alfrescoClass) + { + return removeMixin.get(alfrescoClass); + } + + /** + * Add Mixin Behaviour + * + * Encapsulates mapping of JCR behaviour to Alfresco + */ + public interface AddMixin + { + public Map preAddMixin(SessionImpl session, NodeRef nodeRef); + public void postAddMixin(SessionImpl session, NodeRef nodeRef); + } + + /** + * Remove Mixin Behaviour + * + * Encapsulates mapping of JCR behaviour to Alfresco + */ + public interface RemoveMixin + { + public void preRemoveMixin(SessionImpl session, NodeRef nodeRef); + public void postRemoveMixin(SessionImpl session, NodeRef nodeRef); + } + +} diff --git a/source/java/org/alfresco/jcr/dictionary/DataTypeMap.java b/source/java/org/alfresco/jcr/dictionary/DataTypeMap.java new file mode 100644 index 0000000000..5f1215a998 --- /dev/null +++ b/source/java/org/alfresco/jcr/dictionary/DataTypeMap.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.dictionary; + +import java.util.HashMap; +import java.util.Map; + +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.namespace.QName; + + +/** + * Responsible for mapping Alfresco Data Types to JCR Property Types and vice versa. + * + * @author David Caruana + */ +public class DataTypeMap +{ + + /** Map of Alfresco Data Type to JCR Property Type */ + private static Map dataTypeToPropertyType = new HashMap(); + static + { + dataTypeToPropertyType.put(DataTypeDefinition.TEXT, PropertyType.STRING); + dataTypeToPropertyType.put(DataTypeDefinition.CONTENT, PropertyType.BINARY); + dataTypeToPropertyType.put(DataTypeDefinition.INT, PropertyType.LONG); + dataTypeToPropertyType.put(DataTypeDefinition.LONG, PropertyType.LONG); + dataTypeToPropertyType.put(DataTypeDefinition.FLOAT, PropertyType.DOUBLE); + dataTypeToPropertyType.put(DataTypeDefinition.DOUBLE, PropertyType.DOUBLE); + dataTypeToPropertyType.put(DataTypeDefinition.DATE, PropertyType.DATE); + dataTypeToPropertyType.put(DataTypeDefinition.DATETIME, PropertyType.DATE); + dataTypeToPropertyType.put(DataTypeDefinition.BOOLEAN, PropertyType.BOOLEAN); + dataTypeToPropertyType.put(DataTypeDefinition.QNAME, PropertyType.NAME); + dataTypeToPropertyType.put(DataTypeDefinition.CATEGORY, PropertyType.STRING); // TODO: Check this mapping + dataTypeToPropertyType.put(DataTypeDefinition.NODE_REF, PropertyType.REFERENCE); + dataTypeToPropertyType.put(DataTypeDefinition.PATH, PropertyType.PATH); + dataTypeToPropertyType.put(DataTypeDefinition.ANY, PropertyType.UNDEFINED); + } + + /** Map of JCR Property Type to Alfresco Data Type */ + private static Map propertyTypeToDataType = new HashMap(); + static + { + propertyTypeToDataType.put(PropertyType.STRING, DataTypeDefinition.TEXT); + propertyTypeToDataType.put(PropertyType.BINARY, DataTypeDefinition.CONTENT); + propertyTypeToDataType.put(PropertyType.LONG, DataTypeDefinition.LONG); + propertyTypeToDataType.put(PropertyType.DOUBLE, DataTypeDefinition.DOUBLE); + propertyTypeToDataType.put(PropertyType.DATE, DataTypeDefinition.DATETIME); + propertyTypeToDataType.put(PropertyType.BOOLEAN, DataTypeDefinition.BOOLEAN); + propertyTypeToDataType.put(PropertyType.NAME, DataTypeDefinition.QNAME); + propertyTypeToDataType.put(PropertyType.REFERENCE, DataTypeDefinition.NODE_REF); + propertyTypeToDataType.put(PropertyType.PATH, DataTypeDefinition.PATH); + propertyTypeToDataType.put(PropertyType.UNDEFINED, DataTypeDefinition.ANY); + } + + /** + * Convert an Alfresco Data Type to a JCR Property Type + * + * @param datatype alfresco data type + * @return JCR property type + * @throws RepositoryException + */ + public static int convertDataTypeToPropertyType(QName datatype) + { + Integer propertyType = dataTypeToPropertyType.get(datatype); + if (propertyType == null) + { + throw new AlfrescoRuntimeException("Cannot map Alfresco data type " + datatype + " to JCR property type."); + } + return propertyType; + } + + /** + * Convert a JCR Property Type to an Alfresco Data Type + * + * @param propertyType JCR property type + * @return alfresco data type + * @throws RepositoryException + */ + public static QName convertPropertyTypeToDataType(int propertyType) + { + QName type = propertyTypeToDataType.get(propertyType); + if (type == null) + { + throw new AlfrescoRuntimeException("Cannot map JCR property type " + propertyType + " to Alfresco data type."); + } + return type; + } + +} diff --git a/source/java/org/alfresco/jcr/dictionary/JCRNamespace.java b/source/java/org/alfresco/jcr/dictionary/JCRNamespace.java new file mode 100644 index 0000000000..b7967f929d --- /dev/null +++ b/source/java/org/alfresco/jcr/dictionary/JCRNamespace.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.dictionary; + + +/** + * JCR Namespace definitions + * + * @author David Caruana + */ +public class JCRNamespace +{ + public static String XML_PREFIX = "xml"; + + public static String JCR_URI = "http://www.jcp.org/jcr/1.0"; + public static String JCR_PREFIX = "jcr"; + + public static String NT_URI = "http://www.jcp.org/jcr/nt/1.0"; + public static String NT_PREFIX = "nt"; + + public static String MIX_URI = "http://www.jcp.org/jcr/mix/1.0"; + public static String MIX_PREFIX = "mix"; + + public static String SV_URI = "http://www.jcp.org/jcr/sv/1.0"; + public static String SV_PREFIX = "sv"; +} diff --git a/source/java/org/alfresco/jcr/dictionary/JCRNamespacePrefixResolver.java b/source/java/org/alfresco/jcr/dictionary/JCRNamespacePrefixResolver.java new file mode 100644 index 0000000000..2bdc6aeaa5 --- /dev/null +++ b/source/java/org/alfresco/jcr/dictionary/JCRNamespacePrefixResolver.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.dictionary; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.service.namespace.NamespaceException; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.NamespaceService; + + +/** + * JCR Namespace Resolver + * + * @author David Caruana + */ +public class JCRNamespacePrefixResolver implements NamespaceService +{ + // delegate + private NamespacePrefixResolver delegate; + + // prefix -> uri + private Map prefixes = new HashMap(); + + // uri -> prefix + private Map uris = new HashMap(); + + + /** + * Construct + * + * @param delegate namespace delegate + */ + public JCRNamespacePrefixResolver(NamespacePrefixResolver delegate) + { + this.delegate = delegate; + } + + /* (non-Javadoc) + * @see org.alfresco.service.namespace.NamespacePrefixResolver#getPrefixes(java.lang.String) + */ + public Collection getPrefixes(String namespaceURI) throws NamespaceException + { + String prefix = uris.get(namespaceURI); + if (prefix == null) + { + return delegate.getPrefixes(namespaceURI); + } + List prefixes = new ArrayList(); + prefixes.add(prefix); + return prefixes; + } + + /* (non-Javadoc) + * @see org.alfresco.service.namespace.NamespacePrefixResolver#getPrefixes() + */ + public Collection getPrefixes() + { + List prefixes = new ArrayList(); + Collection uris = getURIs(); + for (String uri : uris) + { + Collection uriPrefixes = getPrefixes(uri); + prefixes.addAll(uriPrefixes); + } + return prefixes; + } + + /* (non-Javadoc) + * @see org.alfresco.service.namespace.NamespaceService#registerNamespace(java.lang.String, java.lang.String) + */ + public void registerNamespace(String prefix, String uri) + { + // + // Check re-mapping according to JCR specification + // + + // Cannot map any prefix that starts with xml + if (prefix.toLowerCase().startsWith(JCRNamespace.XML_PREFIX)) + { + throw new NamespaceException("Cannot map prefix " + prefix + " as it is reserved"); + } + + // Cannot remap a prefix that is already assigned to a uri + String existingUri = delegate.getNamespaceURI(prefix); + if (existingUri != null) + { + throw new NamespaceException("Cannot map prefix " + prefix + " as it is already assigned to uri " + existingUri); + } + + // Cannot map a prefix to a non-existent uri + Collection existingURIs = delegate.getURIs(); + if (existingURIs.contains(uri) == false) + { + throw new NamespaceException("Cannot map prefix " + prefix + " to uri " + uri + " which does not exist"); + } + + prefixes.put(prefix, uri); + uris.put(uri, prefix); + } + + /* (non-Javadoc) + * @see org.alfresco.service.namespace.NamespaceService#unregisterNamespace(java.lang.String) + */ + public void unregisterNamespace(String prefix) + { + String uri = prefixes.get(prefix); + if (uri != null) + { + uris.remove(uri); + } + prefixes.remove(prefix); + } + + public String getNamespaceURI(String prefix) throws NamespaceException + { + String uri = prefixes.get(prefix); + if (uri == null) + { + return delegate.getNamespaceURI(prefix); + } + return uri; + } + + /* (non-Javadoc) + * @see org.alfresco.service.namespace.NamespacePrefixResolver#getURIs() + */ + public Collection getURIs() + { + return delegate.getURIs(); + } + +} diff --git a/source/java/org/alfresco/jcr/dictionary/NamespaceRegistryImpl.java b/source/java/org/alfresco/jcr/dictionary/NamespaceRegistryImpl.java new file mode 100644 index 0000000000..69f948aadd --- /dev/null +++ b/source/java/org/alfresco/jcr/dictionary/NamespaceRegistryImpl.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.dictionary; + +import java.util.Collection; + +import javax.jcr.AccessDeniedException; +import javax.jcr.NamespaceException; +import javax.jcr.NamespaceRegistry; +import javax.jcr.RepositoryException; +import javax.jcr.UnsupportedRepositoryOperationException; + +import org.alfresco.service.namespace.NamespaceService; + + +/** + * Alfresco implementation of a JCR Namespace registry + * + * @author David Caruana + */ +public class NamespaceRegistryImpl implements NamespaceRegistry +{ + + private boolean allowRegistration; + private NamespaceService namespaceService; + + + /** + * Construct + * + * @param namespaceService namespace service + */ + public NamespaceRegistryImpl(boolean allowRegistraton, NamespaceService namespaceService) + { + this.allowRegistration = allowRegistraton; + this.namespaceService = namespaceService; + } + + /** + * Get the namespace prefix resolver + * + * @return the namespace prefix resolver + */ + public NamespaceService getNamespaceService() + { + return this.namespaceService; + } + + /* (non-Javadoc) + * @see javax.jcr.NamespaceRegistry#registerNamespace(java.lang.String, java.lang.String) + */ + public void registerNamespace(String prefix, String uri) throws NamespaceException, UnsupportedRepositoryOperationException, AccessDeniedException, RepositoryException + { + try + { + if (!allowRegistration) + { + throw new UnsupportedRepositoryOperationException(); + } + namespaceService.registerNamespace(prefix, uri); + } + catch(org.alfresco.service.namespace.NamespaceException e) + { + throw new NamespaceException(e); + } + } + + /* (non-Javadoc) + * @see javax.jcr.NamespaceRegistry#unregisterNamespace(java.lang.String) + */ + public void unregisterNamespace(String prefix) throws NamespaceException, UnsupportedRepositoryOperationException, AccessDeniedException, RepositoryException + { + try + { + if (!allowRegistration) + { + throw new UnsupportedRepositoryOperationException(); + } + namespaceService.unregisterNamespace(prefix); + } + catch(org.alfresco.service.namespace.NamespaceException e) + { + throw new NamespaceException(e); + } + } + + /* (non-Javadoc) + * @see javax.jcr.NamespaceRegistry#getPrefixes() + */ + public String[] getPrefixes() throws RepositoryException + { + Collection prefixes = namespaceService.getPrefixes(); + return prefixes.toArray(new String[prefixes.size()]); + } + + /* (non-Javadoc) + * @see javax.jcr.NamespaceRegistry#getURIs() + */ + public String[] getURIs() throws RepositoryException + { + Collection uris = namespaceService.getURIs(); + return uris.toArray(new String[uris.size()]); + } + + /* (non-Javadoc) + * @see javax.jcr.NamespaceRegistry#getURI(java.lang.String) + */ + public String getURI(String prefix) throws NamespaceException, RepositoryException + { + String uri = namespaceService.getNamespaceURI(prefix); + if (uri == null) + { + throw new NamespaceException("Prefix " + prefix + " is unknown."); + } + return uri; + } + + /* (non-Javadoc) + * @see javax.jcr.NamespaceRegistry#getPrefix(java.lang.String) + */ + public String getPrefix(String uri) throws NamespaceException, RepositoryException + { + Collection prefixes = namespaceService.getPrefixes(uri); + if (prefixes.size() == 0) + { + throw new NamespaceException("URI " + uri + " is unknown."); + } + // Return first prefix registered for uri + return prefixes.iterator().next(); + } + +} diff --git a/source/java/org/alfresco/jcr/dictionary/NodeDefinitionImpl.java b/source/java/org/alfresco/jcr/dictionary/NodeDefinitionImpl.java new file mode 100644 index 0000000000..e41feab558 --- /dev/null +++ b/source/java/org/alfresco/jcr/dictionary/NodeDefinitionImpl.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.dictionary; + + +import javax.jcr.nodetype.NodeDefinition; +import javax.jcr.nodetype.NodeType; +import javax.jcr.version.OnParentVersionAction; + +import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; + + +/** + * Alfresco implementation of a JCR Node Definition + * + * @author David Caruana + * + */ +public class NodeDefinitionImpl implements NodeDefinition +{ + private NodeTypeManagerImpl typeManager; + private ChildAssociationDefinition assocDef; + + /** + * Construct + * + * @param typeManager + * @param assocDef + */ + public NodeDefinitionImpl(NodeTypeManagerImpl typeManager, ChildAssociationDefinition assocDef) + { + this.typeManager = typeManager; + this.assocDef = assocDef; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeDefinition#getRequiredPrimaryTypes() + */ + public NodeType[] getRequiredPrimaryTypes() + { + // Note: target class is mandatory in Alfresco + ClassDefinition target = assocDef.getTargetClass(); + return new NodeType[] { typeManager.getNodeTypeImpl(target.getName()) }; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeDefinition#getDefaultPrimaryType() + */ + public NodeType getDefaultPrimaryType() + { + return null; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeDefinition#allowsSameNameSiblings() + */ + public boolean allowsSameNameSiblings() + { + return assocDef.getDuplicateChildNamesAllowed(); + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.ItemDefinition#getDeclaringNodeType() + */ + public NodeType getDeclaringNodeType() + { + return typeManager.getNodeTypeImpl(assocDef.getSourceClass().getName()); + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.ItemDefinition#getName() + */ + public String getName() + { + return assocDef.getName().toPrefixString(typeManager.getNamespaceService()); + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.ItemDefinition#isAutoCreated() + */ + public boolean isAutoCreated() + { + return isMandatory(); + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.ItemDefinition#isMandatory() + */ + public boolean isMandatory() + { + return assocDef.isTargetMandatory(); + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.ItemDefinition#getOnParentVersion() + */ + public int getOnParentVersion() + { + // TODO: Check this correct + return OnParentVersionAction.INITIALIZE; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.ItemDefinition#isProtected() + */ + public boolean isProtected() + { + return assocDef.isProtected(); + } + +} diff --git a/source/java/org/alfresco/jcr/dictionary/NodeTypeImpl.java b/source/java/org/alfresco/jcr/dictionary/NodeTypeImpl.java new file mode 100644 index 0000000000..82ce28c51c --- /dev/null +++ b/source/java/org/alfresco/jcr/dictionary/NodeTypeImpl.java @@ -0,0 +1,426 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.dictionary; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; +import javax.jcr.nodetype.NodeDefinition; +import javax.jcr.nodetype.NodeType; +import javax.jcr.nodetype.PropertyDefinition; + +import org.alfresco.jcr.item.ValueImpl; +import org.alfresco.jcr.item.property.JCRMixinTypesProperty; +import org.alfresco.jcr.item.property.JCRPrimaryTypeProperty; +import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.QName; + +/** + * Alfresco implementation of a Node Type Definition + * + * @author David Caruana + */ +public class NodeTypeImpl implements NodeType +{ + // The required nt:base type specified by JCR + public static QName NT_BASE = QName.createQName(JCRNamespace.NT_URI, "base"); + + // The optional mix:referenceable specified by JCR + public static QName MIX_REFERENCEABLE = QName.createQName(JCRNamespace.MIX_URI, "referenceable"); + // The optional mix:lockable specified by JCR + public static QName MIX_LOCKABLE = QName.createQName(JCRNamespace.MIX_URI, "lockable"); + // The optional mix:versionable specified by JCR + public static QName MIX_VERSIONABLE = QName.createQName(JCRNamespace.MIX_URI, "versionable"); + + + private NodeTypeManagerImpl typeManager; + private ClassDefinition classDefinition; + + + /** + * Construct + * + * @param classDefinition Alfresco class definition + */ + public NodeTypeImpl(NodeTypeManagerImpl typeManager, ClassDefinition classDefinition) + { + this.typeManager = typeManager; + this.classDefinition = classDefinition; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeType#getName() + */ + public String getName() + { + return classDefinition.getName().toPrefixString(typeManager.getNamespaceService()); + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeType#isMixin() + */ + public boolean isMixin() + { + return classDefinition.isAspect(); + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeType#hasOrderableChildNodes() + */ + public boolean hasOrderableChildNodes() + { + // Note: For now, we don't expose this through JCR + return false; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeType#getPrimaryItemName() + */ + public String getPrimaryItemName() + { + // NOTE: Alfresco does not support the notion of PrimaryItem (not yet anyway) + return null; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeType#getSupertypes() + */ + public NodeType[] getSupertypes() + { + List nodeTypes = new ArrayList(); + NodeType[] declaredSupertypes = getDeclaredSupertypes(); + while (declaredSupertypes.length > 0) + { + // Alfresco supports single inheritence only + NodeType supertype = declaredSupertypes[0]; + nodeTypes.add(supertype); + declaredSupertypes = supertype.getDeclaredSupertypes(); + } + return nodeTypes.toArray(new NodeType[nodeTypes.size()]); + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeType#getDeclaredSupertypes() + */ + public NodeType[] getDeclaredSupertypes() + { + // return no supertype when type is nt:base + if (classDefinition.getName().equals(NT_BASE)) + { + return new NodeType[] {}; + } + + // return root type when no parent (nt:base if a type hierarchy) + QName parent = classDefinition.getParentName(); + if (parent == null) + { + if (classDefinition.isAspect()) + { + return new NodeType[] {}; + } + else + { + return new NodeType[] { typeManager.getNodeTypeImpl(NT_BASE) }; + } + } + + // return the supertype + return new NodeType[] { typeManager.getNodeTypeImpl(parent) }; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeType#isNodeType(java.lang.String) + */ + public boolean isNodeType(String nodeTypeName) + { + QName name = QName.createQName(nodeTypeName, typeManager.getNamespaceService()); + + // is it one of standard types + if (name.equals(NodeTypeImpl.NT_BASE)) + { + return true; + } + + // is it part of this class hierarchy + return typeManager.getSession().getRepositoryImpl().getServiceRegistry().getDictionaryService().isSubClass(name, classDefinition.getName()); + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeType#getPropertyDefinitions() + */ + public PropertyDefinition[] getPropertyDefinitions() + { + Map propDefs = classDefinition.getProperties(); + PropertyDefinition[] defs = new PropertyDefinition[propDefs.size() + (classDefinition.isAspect() ? 0 : 2)]; + int i = 0; + for (org.alfresco.service.cmr.dictionary.PropertyDefinition propDef : propDefs.values()) + { + defs[i++] = new PropertyDefinitionImpl(typeManager, propDef); + } + + if (!classDefinition.isAspect()) + { + // add nt:base properties + defs[i++] = typeManager.getPropertyDefinitionImpl(JCRPrimaryTypeProperty.PROPERTY_NAME); + defs[i++] = typeManager.getPropertyDefinitionImpl(JCRMixinTypesProperty.PROPERTY_NAME); + } + + return defs; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeType#getDeclaredPropertyDefinitions() + */ + public PropertyDefinition[] getDeclaredPropertyDefinitions() + { + Map propDefs = classDefinition.getProperties(); + List defs = new ArrayList(); + for (org.alfresco.service.cmr.dictionary.PropertyDefinition propDef : propDefs.values()) + { + if (propDef.getContainerClass().equals(classDefinition)) + { + defs.add(new PropertyDefinitionImpl(typeManager, propDef)); + } + } + + if (classDefinition.equals(NT_BASE)) + { + // add nt:base properties + defs.add(typeManager.getPropertyDefinitionImpl(JCRPrimaryTypeProperty.PROPERTY_NAME)); + defs.add(typeManager.getPropertyDefinitionImpl(JCRMixinTypesProperty.PROPERTY_NAME)); + } + + return defs.toArray(new PropertyDefinition[defs.size()]); + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeType#getChildNodeDefinitions() + */ + public NodeDefinition[] getChildNodeDefinitions() + { + Map assocDefs = classDefinition.getChildAssociations(); + NodeDefinition[] defs = new NodeDefinition[assocDefs.size()]; + int i = 0; + for (ChildAssociationDefinition assocDef : assocDefs.values()) + { + defs[i++] = new NodeDefinitionImpl(typeManager, assocDef); + } + return defs; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeType#getDeclaredChildNodeDefinitions() + */ + public NodeDefinition[] getDeclaredChildNodeDefinitions() + { + Map assocDefs = classDefinition.getChildAssociations(); + List defs = new ArrayList(); + for (ChildAssociationDefinition assocDef : assocDefs.values()) + { + if (assocDef.getSourceClass().equals(classDefinition)) + { + defs.add(new NodeDefinitionImpl(typeManager, assocDef)); + } + } + return defs.toArray(new NodeDefinition[defs.size()]); + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeType#canSetProperty(java.lang.String, javax.jcr.Value) + */ + public boolean canSetProperty(String propertyName, Value value) + { + try + { + // is an attempt to remove property being made + if (value == null) + { + return canRemoveItem(propertyName); + } + + // retrieve property definition + QName propertyQName = QName.createQName(propertyName, typeManager.getNamespaceService()); + Map propDefs = classDefinition.getProperties(); + org.alfresco.service.cmr.dictionary.PropertyDefinition propDef = propDefs.get(propertyQName); + if (propDef == null) + { + // Alfresco doesn't have residual properties yet + return false; + } + + // is property read-write + if (propDef.isProtected() || propDef.isMultiValued()) + { + return false; + } + + // get required type to convert to + int requiredType = DataTypeMap.convertDataTypeToPropertyType(propDef.getDataType().getName()); + if (requiredType == PropertyType.UNDEFINED) + { + requiredType = value.getType(); + } + + // convert value to required type + // Note: Invalid conversion will throw exception + ValueImpl.getValue(typeManager.getSession().getTypeConverter(), requiredType, value); + + // Note: conversion succeeded + return true; + } + catch(RepositoryException e) + { + // Note: Not much can be done really + } + + return false; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeType#canSetProperty(java.lang.String, javax.jcr.Value[]) + */ + public boolean canSetProperty(String propertyName, Value[] values) + { + try + { + // is an attempt to remove property being made + if (values == null) + { + return canRemoveItem(propertyName); + } + + // retrieve property definition + QName propertyQName = QName.createQName(propertyName, typeManager.getNamespaceService()); + Map propDefs = classDefinition.getProperties(); + org.alfresco.service.cmr.dictionary.PropertyDefinition propDef = propDefs.get(propertyQName); + if (propDef == null) + { + // Alfresco doesn't have residual properties yet + return false; + } + + // is property read write + if (propDef.isProtected() || !propDef.isMultiValued()) + { + return false; + } + + // determine type of values to check + int valueType = PropertyType.UNDEFINED; + for (Value value : values) + { + if (value != null) + { + if (valueType != PropertyType.UNDEFINED && value.getType() != valueType) + { + // do not allow collection mixed type values + return false; + } + valueType = value.getType(); + } + } + + // get required type to convert to + int requiredType = DataTypeMap.convertDataTypeToPropertyType(propDef.getDataType().getName()); + if (requiredType == PropertyType.UNDEFINED) + { + requiredType = valueType; + } + + // convert values to required format + // Note: Invalid conversion will throw exception + for (Value value : values) + { + if (value != null) + { + ValueImpl.getValue(typeManager.getSession().getTypeConverter(), requiredType, value); + } + } + + // Note: conversion succeeded + return true; + } + catch(RepositoryException e) + { + // Note: Not much can be done really + } + + return false; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeType#canAddChildNode(java.lang.String) + */ + public boolean canAddChildNode(String childNodeName) + { + // NOTE: Alfresco does not have default primary type notion + return false; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeType#canAddChildNode(java.lang.String, java.lang.String) + */ + public boolean canAddChildNode(String childNodeName, String nodeTypeName) + { + boolean canAdd = false; + Map assocDefs = classDefinition.getChildAssociations(); + QName childNodeQName = QName.createQName(childNodeName, typeManager.getNamespaceService()); + ChildAssociationDefinition assocDef = assocDefs.get(childNodeQName); + if (assocDef != null) + { + QName nodeTypeQName = QName.createQName(nodeTypeName, typeManager.getNamespaceService()); + DictionaryService dictionaryService = typeManager.getSession().getRepositoryImpl().getServiceRegistry().getDictionaryService(); + canAdd = dictionaryService.isSubClass(nodeTypeQName, assocDef.getTargetClass().getName()); + } + return canAdd; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeType#canRemoveItem(java.lang.String) + */ + public boolean canRemoveItem(String itemName) + { + boolean isProtected = false; + boolean isMandatory = false; + + // TODO: Property and Association names can clash? What to do? + QName itemQName = QName.createQName(itemName, typeManager.getNamespaceService()); + Map propDefs = classDefinition.getProperties(); + org.alfresco.service.cmr.dictionary.PropertyDefinition propDef = propDefs.get(itemQName); + if (propDef != null) + { + isProtected = propDef.isProtected(); + isMandatory = propDef.isMandatory(); + } + Map assocDefs = classDefinition.getChildAssociations(); + ChildAssociationDefinition assocDef = assocDefs.get(itemQName); + if (assocDef != null) + { + isProtected |= assocDef.isProtected(); + isMandatory |= assocDef.isTargetMandatory(); + } + + return !isProtected && !isMandatory; + } + +} diff --git a/source/java/org/alfresco/jcr/dictionary/NodeTypeManagerImpl.java b/source/java/org/alfresco/jcr/dictionary/NodeTypeManagerImpl.java new file mode 100644 index 0000000000..f8410f6bc9 --- /dev/null +++ b/source/java/org/alfresco/jcr/dictionary/NodeTypeManagerImpl.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.dictionary; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.jcr.RepositoryException; +import javax.jcr.nodetype.NoSuchNodeTypeException; +import javax.jcr.nodetype.NodeType; +import javax.jcr.nodetype.NodeTypeIterator; +import javax.jcr.nodetype.NodeTypeManager; + +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + + +/** + * Alfresco implementation of JCR Node Type Manager + * + * @author David Caruana + */ +public class NodeTypeManagerImpl implements NodeTypeManager +{ + private SessionImpl session; + private NamespaceService namespaceService; + + /** + * Construct + * + * @param dictionaryService dictionary service + * @param namespaceService namespace service (global repository registry) + */ + public NodeTypeManagerImpl(SessionImpl session, NamespaceService namespaceService) + { + this.session = session; + this.namespaceService = namespaceService; + } + + /** + * Get Dictionary Service + * + * @return the dictionary service + */ + public SessionImpl getSession() + { + return session; + } + + /** + * Get Namespace Service + * + * @return the namespace service + */ + public NamespaceService getNamespaceService() + { + return namespaceService; + } + + /** + * Get Node Type Implementation for given Class Name + * + * @param nodeTypeName alfresco class name + * @return the node type + */ + public NodeTypeImpl getNodeTypeImpl(QName nodeTypeName) + { + // TODO: Might be worth caching here... wait and see + NodeTypeImpl nodeType = null; + ClassDefinition definition = session.getRepositoryImpl().getServiceRegistry().getDictionaryService().getClass(nodeTypeName); + if (definition != null) + { + nodeType = new NodeTypeImpl(this, definition); + } + return nodeType; + } + + /** + * Get Property Definition Implementation for given Property Name + * + * @param propertyName alfresco property name + * @return the property + */ + public PropertyDefinitionImpl getPropertyDefinitionImpl(QName propertyName) + { + // TODO: Might be worth caching here... wait and see + PropertyDefinitionImpl propDef = null; + PropertyDefinition definition = session.getRepositoryImpl().getServiceRegistry().getDictionaryService().getProperty(propertyName); + if (definition != null) + { + propDef = new PropertyDefinitionImpl(this, definition); + } + return propDef; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeTypeManager#getNodeType(java.lang.String) + */ + public NodeType getNodeType(String nodeTypeName) throws NoSuchNodeTypeException, RepositoryException + { + QName name = QName.createQName(nodeTypeName, namespaceService); + NodeTypeImpl nodeTypeImpl = getNodeTypeImpl(name); + if (nodeTypeImpl == null) + { + throw new NoSuchNodeTypeException("Node type " + nodeTypeName + " does not exist"); + } + return nodeTypeImpl; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeTypeManager#getAllNodeTypes() + */ + public NodeTypeIterator getAllNodeTypes() throws RepositoryException + { + Collection typeNames = session.getRepositoryImpl().getServiceRegistry().getDictionaryService().getAllTypes(); + Collection aspectNames = session.getRepositoryImpl().getServiceRegistry().getDictionaryService().getAllAspects(); + List typesList = new ArrayList(typeNames.size() + aspectNames.size()); + typesList.addAll(typeNames); + typesList.addAll(aspectNames); + return new NodeTypeNameIterator(this, typesList); + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeTypeManager#getPrimaryNodeTypes() + */ + public NodeTypeIterator getPrimaryNodeTypes() throws RepositoryException + { + Collection typeNames = session.getRepositoryImpl().getServiceRegistry().getDictionaryService().getAllTypes(); + List typesList = new ArrayList(typeNames.size()); + typesList.addAll(typeNames); + return new NodeTypeNameIterator(this, typesList); + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeTypeManager#getMixinNodeTypes() + */ + public NodeTypeIterator getMixinNodeTypes() throws RepositoryException + { + Collection typeNames = session.getRepositoryImpl().getServiceRegistry().getDictionaryService().getAllAspects(); + List typesList = new ArrayList(typeNames.size()); + typesList.addAll(typeNames); + return new NodeTypeNameIterator(this, typesList); + } + +} diff --git a/source/java/org/alfresco/jcr/dictionary/NodeTypeNameIterator.java b/source/java/org/alfresco/jcr/dictionary/NodeTypeNameIterator.java new file mode 100644 index 0000000000..fe96982f09 --- /dev/null +++ b/source/java/org/alfresco/jcr/dictionary/NodeTypeNameIterator.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.dictionary; + +import java.util.List; + +import javax.jcr.nodetype.NodeType; +import javax.jcr.nodetype.NodeTypeIterator; + +import org.alfresco.jcr.util.AbstractRangeIterator; +import org.alfresco.service.namespace.QName; + + +/** + * Alfresco implementation of a Node Type Iterator + * + * @author David Caruana + */ +public class NodeTypeNameIterator extends AbstractRangeIterator + implements NodeTypeIterator +{ + private NodeTypeManagerImpl typeManager; + private List nodeTypeNames; + + + /** + * Construct + * + * @param context session context + * @param nodeTypes node type list + */ + public NodeTypeNameIterator(NodeTypeManagerImpl typeManager, List nodeTypeNames) + { + this.typeManager = typeManager; + this.nodeTypeNames = nodeTypeNames; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.NodeTypeIterator#nextNodeType() + */ + public NodeType nextNodeType() + { + long position = skip(); + QName name = nodeTypeNames.get((int)position); + return typeManager.getNodeTypeImpl(name); + } + + /* (non-Javadoc) + * @see javax.jcr.RangeIterator#getSize() + */ + public long getSize() + { + return nodeTypeNames.size(); + } + + /* (non-Javadoc) + * @see java.util.Iterator#next() + */ + public Object next() + { + return nextNodeType(); + } + +} diff --git a/source/java/org/alfresco/jcr/dictionary/PropertyDefinitionImpl.java b/source/java/org/alfresco/jcr/dictionary/PropertyDefinitionImpl.java new file mode 100644 index 0000000000..39f9a4161c --- /dev/null +++ b/source/java/org/alfresco/jcr/dictionary/PropertyDefinitionImpl.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.dictionary; + +import javax.jcr.Value; +import javax.jcr.nodetype.NodeType; +import javax.jcr.nodetype.PropertyDefinition; +import javax.jcr.version.OnParentVersionAction; + +import org.alfresco.jcr.item.ValueImpl; +import org.alfresco.jcr.item.property.JCRMixinTypesProperty; +import org.alfresco.jcr.item.property.JCRPrimaryTypeProperty; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; + +/** + * Alfresco implementation of a JCR Property Definition + * + * @author David Caruana + */ +public class PropertyDefinitionImpl implements PropertyDefinition +{ + /** Session */ + private NodeTypeManagerImpl typeManager; + + /** Alfresco Property Definition */ + private org.alfresco.service.cmr.dictionary.PropertyDefinition propDef; + + + /** + * Construct + * + * @param propDef Alfresco Property Definition + */ + public PropertyDefinitionImpl(NodeTypeManagerImpl typeManager, org.alfresco.service.cmr.dictionary.PropertyDefinition propDef) + { + this.typeManager = typeManager; + this.propDef = propDef; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.PropertyDefinition#getRequiredType() + */ + public int getRequiredType() + { + // TODO: Switch on data type + if (propDef.getName().equals(ContentModel.PROP_CONTENT)) + { + return DataTypeMap.convertDataTypeToPropertyType(DataTypeDefinition.CONTENT); + } + return DataTypeMap.convertDataTypeToPropertyType(propDef.getDataType().getName()); + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.PropertyDefinition#getValueConstraints() + */ + public String[] getValueConstraints() + { + return new String[] {}; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.PropertyDefinition#getDefaultValues() + */ + public Value[] getDefaultValues() + { + String defaultValue = propDef.getDefaultValue(); + if (defaultValue == null) + { + return null; + } + return new Value[] { new ValueImpl(typeManager.getSession(), getRequiredType(), defaultValue) }; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.PropertyDefinition#isMultiple() + */ + public boolean isMultiple() + { + return propDef.isMultiValued(); + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.ItemDefinition#getDeclaringNodeType() + */ + public NodeType getDeclaringNodeType() + { + ClassDefinition declaringClass = propDef.getContainerClass(); + return typeManager.getNodeTypeImpl(declaringClass.getName()); + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.ItemDefinition#getName() + */ + public String getName() + { + return propDef.getName().toPrefixString(typeManager.getNamespaceService()); + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.ItemDefinition#isAutoCreated() + */ + public boolean isAutoCreated() + { + return isMandatory(); + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.ItemDefinition#isMandatory() + */ + public boolean isMandatory() + { + return propDef.isMandatory(); + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.ItemDefinition#getOnParentVersion() + */ + public int getOnParentVersion() + { + // TODO: There's no equivalent in Alfresco, so hard code for now + if (propDef.getName().equals(JCRPrimaryTypeProperty.PROPERTY_NAME) || + propDef.getName().equals(JCRMixinTypesProperty.PROPERTY_NAME)) + { + return OnParentVersionAction.COMPUTE; + } + + // TODO: Check this + return OnParentVersionAction.INITIALIZE; + } + + /* (non-Javadoc) + * @see javax.jcr.nodetype.ItemDefinition#isProtected() + */ + public boolean isProtected() + { + return propDef.isProtected(); + } + +} diff --git a/source/java/org/alfresco/jcr/dictionary/VersionableMixin.java b/source/java/org/alfresco/jcr/dictionary/VersionableMixin.java new file mode 100644 index 0000000000..c924ac6f8a --- /dev/null +++ b/source/java/org/alfresco/jcr/dictionary/VersionableMixin.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.dictionary; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + + +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * Encapsulate Versionable Mixin behaviour mapping to Alfresco + * + * @author davidc + */ +public class VersionableMixin implements ClassMap.AddMixin, ClassMap.RemoveMixin +{ + + /* + * (non-Javadoc) + * @see org.alfresco.jcr.dictionary.ClassMap.AddMixin#preAddMixin(org.alfresco.jcr.session.SessionImpl, org.alfresco.service.cmr.repository.NodeRef) + */ + public Map preAddMixin(SessionImpl session, NodeRef nodeRef) + { + // switch off auto-versioning + Map properties = new HashMap(); + properties.put(ContentModel.PROP_INITIAL_VERSION, false); + properties.put(ContentModel.PROP_AUTO_VERSION, false); + return properties; + } + + /* + * (non-Javadoc) + * @see org.alfresco.jcr.dictionary.ClassMap.AddMixin#postAddMixin(org.alfresco.jcr.session.SessionImpl, org.alfresco.service.cmr.repository.NodeRef) + */ + public void postAddMixin(SessionImpl session, NodeRef nodeRef) + { + } + + /* + * (non-Javadoc) + * @see org.alfresco.jcr.dictionary.ClassMap.RemoveMixin#preRemoveMixin(org.alfresco.jcr.session.SessionImpl, org.alfresco.service.cmr.repository.NodeRef) + */ + public void preRemoveMixin(SessionImpl session, NodeRef nodeRef) + { + } + + /* + * (non-Javadoc) + * @see org.alfresco.jcr.dictionary.ClassMap.RemoveMixin#postRemoveMixin(org.alfresco.jcr.session.SessionImpl, org.alfresco.service.cmr.repository.NodeRef) + */ + public void postRemoveMixin(SessionImpl session, NodeRef nodeRef) + { + } + +} diff --git a/source/java/org/alfresco/jcr/example/MixedExample.java b/source/java/org/alfresco/jcr/example/MixedExample.java new file mode 100644 index 0000000000..9c7f1f41f2 --- /dev/null +++ b/source/java/org/alfresco/jcr/example/MixedExample.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.example; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.Repository; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; + +import org.alfresco.jcr.api.JCRNodeRef; +import org.alfresco.model.ContentModel; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + + + +/** + * Example that demonstrate use of JCR and Alfresco API calls. + * + * @author David Caruana + */ +public class MixedExample +{ + + public static void main(String[] args) + throws Exception + { + // Setup Spring and Transaction Service + ApplicationContext context = new ClassPathXmlApplicationContext("classpath:alfresco/application-context.xml"); + ServiceRegistry registry = (ServiceRegistry)context.getBean(ServiceRegistry.SERVICE_REGISTRY); + NodeService nodeService = (NodeService)registry.getNodeService(); + + // Retrieve Repository + Repository repository = (Repository)context.getBean("JCR.Repository"); + + // Login to workspace + // Note: Default workspace is the one used by Alfresco Web Client which contains all the Spaces + // and their documents + Session session = repository.login(new SimpleCredentials("admin", "admin".toCharArray())); + + try + { + // Retrieve Company Home + Node root = session.getRootNode(); + Node companyHome = root.getNode("app:company_home"); + + // Read Company Home Name + Property name = companyHome.getProperty("cm:name"); + System.out.println("Name = " + name.getString()); + + // Update Node via Alfresco Node Service API + NodeRef companyHomeRef = JCRNodeRef.getNodeRef(companyHome); + nodeService.setProperty(companyHomeRef, ContentModel.PROP_NAME, "Updated Company Home Name"); + + // Re-read via JCR + System.out.println("Updated name = " + name.getString()); + } + finally + { + session.logout(); + System.exit(0); + } + } + +} diff --git a/source/java/org/alfresco/jcr/example/SimpleExample.java b/source/java/org/alfresco/jcr/example/SimpleExample.java new file mode 100644 index 0000000000..fdb9f28751 --- /dev/null +++ b/source/java/org/alfresco/jcr/example/SimpleExample.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.example; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.Repository; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + + + +/** + * Simple Example that demonstrate login and retrieval of top-level Spaces + * under Company Home. + * + * @author David Caruana + */ +public class SimpleExample +{ + + public static void main(String[] args) + throws Exception + { + // Setup Spring and Transaction Service + ApplicationContext context = new ClassPathXmlApplicationContext("classpath:alfresco/application-context.xml"); + + // Retrieve Repository + Repository repository = (Repository)context.getBean("JCR.Repository"); + + // Login to workspace + // Note: Default workspace is the one used by Alfresco Web Client which contains all the Spaces + // and their documents + Session session = repository.login(new SimpleCredentials("admin", "admin".toCharArray())); + + try + { + // Retrieve Company Home + Node root = session.getRootNode(); + Node companyHome = root.getNode("app:company_home"); + + // Iterator through children of Company Home + NodeIterator iterator = companyHome.getNodes(); + while(iterator.hasNext()) + { + Node child = iterator.nextNode(); + System.out.println(child.getName()); + + PropertyIterator propIterator = child.getProperties(); + while(propIterator.hasNext()) + { + Property prop = propIterator.nextProperty(); + if (!prop.getDefinition().isMultiple()) + { + System.out.println(" " + prop.getName() + " = " + prop.getString()); + } + } + } + } + finally + { + session.logout(); + System.exit(0); + } + + } + +} diff --git a/source/java/org/alfresco/jcr/example/WIKIExample.java b/source/java/org/alfresco/jcr/example/WIKIExample.java new file mode 100644 index 0000000000..76ae7a8908 --- /dev/null +++ b/source/java/org/alfresco/jcr/example/WIKIExample.java @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.example; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.Calendar; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.PathNotFoundException; +import javax.jcr.Property; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; +import javax.jcr.Value; +import javax.jcr.Workspace; +import javax.jcr.query.Query; +import javax.jcr.query.QueryManager; +import javax.jcr.query.QueryResult; +import javax.jcr.version.Version; +import javax.jcr.version.VersionHistory; +import javax.jcr.version.VersionIterator; + +import org.alfresco.jcr.api.JCRNodeRef; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.PermissionService; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.core.io.ClassPathResource; + + +/** + * Example that demonstrates read and write of a simple WIKI model + * + * Please refer to http://www.alfresco.org/mediawiki/index.php/Introducing_the_Alfresco_Java_Content_Repository_API + * for a complete description of this example. + * + * @author David Caruana + */ +public class WIKIExample +{ + + public static void main(String[] args) + throws Exception + { + // + // Repository Initialisation + // + + // access the Alfresco JCR Repository (here it's via programmatic approach, but it could also be injected) + System.out.println("Initialising Repository..."); + ApplicationContext context = new ClassPathXmlApplicationContext("classpath:org/alfresco/jcr/example/wiki-context.xml"); + Repository repository = (Repository)context.getBean("JCR.Repository"); + + // display information about the repository + System.out.println("Repository Description..."); + String[] keys = repository.getDescriptorKeys(); + for (String key : keys) + { + String value = repository.getDescriptor(key); + System.out.println(" " + key + " = " + value); + } + + // + // Create a WIKI structure + // + // Note: Here we're using the Alfresco Content Model and custom WIKI model to create + // WIKI pages and Content that are accessible via the Alfresco Web Client + // + + // login to workspace (here we rely on the default workspace defined by JCR.Repository bean) + Session session = repository.login(new SimpleCredentials("admin", "admin".toCharArray())); + + try + { + System.out.println("Creating WIKI..."); + + // first, access the company home + Node rootNode = session.getRootNode(); + System.out.println("Root node: path=" + rootNode.getPath() + ", type=" + rootNode.getPrimaryNodeType().getName()); + Node companyHome = rootNode.getNode("app:company_home"); + System.out.println("Company home node: path=" + companyHome.getPath() + ", type=" + companyHome.getPrimaryNodeType().getName()); + + // remove the WIKI structure if it already exists + try + { + Node encyclopedia = companyHome.getNode("wiki:encyclopedia"); + encyclopedia.remove(); + System.out.println("Existing WIKI found and removed"); + } + catch(PathNotFoundException e) + { + // doesn't exist, no need to remove + } + + // create the root WIKI folder + Node encyclopedia = companyHome.addNode("wiki:encyclopedia", "cm:folder"); + encyclopedia.setProperty("cm:name", "WIKI Encyclopedia"); + encyclopedia.setProperty("cm:description", ""); + + // create first wiki page + Node page1 = encyclopedia.addNode("wiki:entry1", "wiki:page"); + page1.setProperty("cm:name", "Rose"); + page1.setProperty("cm:description", ""); + page1.setProperty("cm:title", "The rose"); + page1.setProperty("cm:content", "A rose is a flowering shrub."); + page1.setProperty("wiki:category", new String[] {"flower", "plant", "rose"}); + + // create second wiki page + Node page2 = encyclopedia.addNode("wiki:entry2", "wiki:page"); + page2.setProperty("cm:name", "Shakespeare"); + page2.setProperty("cm:description", ""); + page2.setProperty("cm:title", "William Shakespeare"); + page2.setProperty("cm:content", "A famous poet who likes roses."); + page2.setProperty("wiki:restrict", true); + page2.setProperty("wiki:category", new String[] {"poet"}); + + // create an image (note: we're using an input stream to allow setting of binary content) + Node contentNode = encyclopedia.addNode("wiki:image", "cm:content"); + contentNode.setProperty("cm:name", "Dog"); + contentNode.setProperty("cm:description", ""); + contentNode.setProperty("cm:title", "My dog at New Year party"); + ClassPathResource resource = new ClassPathResource("org/alfresco/jcr/example/wikiImage.gif"); + contentNode.setProperty("cm:content", resource.getInputStream()); + + session.save(); + System.out.println("WIKI created"); + } + finally + { + session.logout(); + } + + // + // Access the WIKI structure + // + + // login to workspace (here we rely on the default workspace defined by JCR.Repository bean) + session = repository.login(new SimpleCredentials("admin", "admin".toCharArray())); + + try + { + System.out.println("Accessing WIKI..."); + + // access a wiki node directly from root node (by path and by UUID) + Node rootNode = session.getRootNode(); + Node encyclopedia = rootNode.getNode("app:company_home/wiki:encyclopedia"); + Node direct = session.getNodeByUUID(encyclopedia.getUUID()); + System.out.println("Found WIKI root correctly: " + encyclopedia.equals(direct)); + + // access a wiki property directly from root node + Node entry1 = rootNode.getNode("app:company_home/wiki:encyclopedia/wiki:entry1"); + String title = entry1.getProperty("cm:title").getString(); + System.out.println("Found WIKI page 1 title: " + title); + Calendar modified = entry1.getProperty("cm:modified").getDate(); + System.out.println("Found WIKI page 1 last modified date: " + modified.getTime()); + + // browse all wiki entries + System.out.println("WIKI browser:"); + NodeIterator entries = encyclopedia.getNodes(); + while (entries.hasNext()) + { + Node entry = entries.nextNode(); + outputContentNode(entry); + } + + // perform a search + System.out.println("Search results:"); + Workspace workspace = session.getWorkspace(); + QueryManager queryManager = workspace.getQueryManager(); + Query query = queryManager.createQuery("//app:company_home/wiki:encyclopedia/*[@cm:title = 'The rose']", Query.XPATH); + //Query query = queryManager.createQuery("//app:company_home/wiki:encyclopedia/*[jcr:contains(., 'rose')]", Query.XPATH); + QueryResult result = query.execute(); + NodeIterator it = result.getNodes(); + while (it.hasNext()) + { + Node n = it.nextNode(); + outputContentNode(n); + } + + // export content (system view format) + File systemView = new File("systemview.xml"); + FileOutputStream systemViewOut = new FileOutputStream(systemView); + session.exportSystemView("/app:company_home/wiki:encyclopedia", systemViewOut, false, false); + + // export content (document view format) + File docView = new File("docview.xml"); + FileOutputStream docViewOut = new FileOutputStream(docView); + session.exportDocumentView("/app:company_home/wiki:encyclopedia", docViewOut, false, false); + + System.out.println("WIKI exported"); + + } + finally + { + session.logout(); + } + + + // + // Advanced Usage + // + + // 1) Check-out / Check-in and version history retrieval + session = repository.login(new SimpleCredentials("admin", "admin".toCharArray())); + + try + { + // + // Version WIKI Page 1 + // + + // first, access the page + Node rootNode = session.getRootNode(); + Node entry1 = rootNode.getNode("app:company_home/wiki:encyclopedia/wiki:entry1"); + + // enable versioning capability + entry1.addMixin("mix:versionable"); + + // update the properties and content + entry1.setProperty("cm:title", "The Rise"); + entry1.setProperty("cm:content", "A rose is a flowering shrub of the genus Rosa."); + Value[] categories = entry1.getProperty("wiki:category").getValues(); + Value[] newCategories = new Value[categories.length + 1]; + System.arraycopy(categories, 0, newCategories, 0, categories.length); + newCategories[categories.length] = session.getValueFactory().createValue("poet"); + entry1.setProperty("wiki:category", newCategories); + + // and checkin the changes + entry1.checkin(); + + // checkout, fix wiki title and checkin again + entry1.checkout(); + entry1.setProperty("cm:title", "The Rose"); + entry1.checkin(); + + session.save(); + System.out.println("Versioned WIKI Page 1"); + } + finally + { + session.logout(); + } + + // 2) Permission checks + session = repository.login(new SimpleCredentials("admin", "admin".toCharArray())); + + try + { + // + // Browse WIKI Page 1 Version History + // + + // first, access the page + Node rootNode = session.getRootNode(); + Node entry1 = rootNode.getNode("app:company_home/wiki:encyclopedia/wiki:entry1"); + + // retrieve the history for thte page + VersionHistory versionHistory = entry1.getVersionHistory(); + VersionIterator versionIterator = versionHistory.getAllVersions(); + + // for each version, output the node as it was versioned + while (versionIterator.hasNext()) + { + Version version = versionIterator.nextVersion(); + NodeIterator nodeIterator = version.getNodes(); + + while (nodeIterator.hasNext()) + { + Node versionedNode = nodeIterator.nextNode(); + System.out.println(" Version: " + version.getName()); + System.out.println(" Created: " + version.getCreated().getTime()); + outputContentNode(versionedNode); + } + } + + + // + // Permission Checks + // + + System.out.println("Testing Permissions:"); + + // check for JCR 'read' permission + session.checkPermission("app:company_home/wiki:encyclopedia/wiki:entry1", "read"); + System.out.println("Session has 'read' permission on app:company_home/wiki:encyclopedia/wiki:entry1"); + + // check for Alfresco 'Take Ownership' permission + session.checkPermission("app:company_home/wiki:encyclopedia/wiki:entry1", PermissionService.TAKE_OWNERSHIP); + System.out.println("Session has 'take ownership' permission on app:company_home/wiki:encyclopedia/wiki:entry1"); + } + finally + { + session.logout(); + } + + + // + // Mixing JCR and Alfresco API calls + // + // Provide mimetype for WIKI content properties + // + + session = repository.login(new SimpleCredentials("admin", "admin".toCharArray())); + + try + { + // Retrieve the Alfresco Repository Service Registry + ServiceRegistry registry = (ServiceRegistry)context.getBean(ServiceRegistry.SERVICE_REGISTRY); + + // set the mime type on both WIKI pages and Image + Node rootNode = session.getRootNode(); + + // note: we have to checkout entry1 first - it's versioned + Node entry1 = rootNode.getNode("app:company_home/wiki:encyclopedia/wiki:entry1"); + entry1.checkout(); + setMimetype(registry, entry1, "cm:content", MimetypeMap.MIMETYPE_TEXT_PLAIN); + entry1.checkin(); + + Node entry2 = rootNode.getNode("app:company_home/wiki:encyclopedia/wiki:entry2"); + setMimetype(registry, entry2, "cm:content", MimetypeMap.MIMETYPE_TEXT_PLAIN); + Node image = rootNode.getNode("app:company_home/wiki:encyclopedia/wiki:image"); + setMimetype(registry, image, "cm:content", MimetypeMap.MIMETYPE_IMAGE_GIF); + + // save the changes + session.save(); + System.out.println("Updated WIKI mimetypes via Alfresco calls"); + } + finally + { + session.logout(); + } + + // exit + System.out.println("Completed successfully."); + System.exit(0); + } + + + private static void outputContentNode(Node node) + throws RepositoryException + { + // output common content properties + System.out.println(" Node " + node.getUUID()); + System.out.println(" title: " + node.getProperty("cm:title").getString()); + + // output properties specific to WIKI page + if (node.getPrimaryNodeType().getName().equals("wiki:page")) + { + System.out.println(" content: " + node.getProperty("cm:content").getString()); + System.out.println(" restrict: " + node.getProperty("wiki:restrict").getString()); + + // output multi-value property + Property categoryProperty = node.getProperty("wiki:category"); + Value[] categories = categoryProperty.getValues(); + for (Value category : categories) + { + System.out.println(" category: " + category.getString()); + } + } + } + + + private static void setMimetype(ServiceRegistry registry, Node node, String propertyName, String mimeType) + throws RepositoryException + { + // convert the JCR Node to an Alfresco Node Reference + NodeRef nodeRef = JCRNodeRef.getNodeRef(node); + + // retrieve the Content Property (represented as a ContentData object in Alfresco) + NodeService nodeService = registry.getNodeService(); + ContentData content = (ContentData)nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); + + // update the Mimetype + content = ContentData.setMimetype(content, mimeType); + nodeService.setProperty(nodeRef, ContentModel.PROP_CONTENT, content); + } + +} diff --git a/source/java/org/alfresco/jcr/example/wiki-context.xml b/source/java/org/alfresco/jcr/example/wiki-context.xml new file mode 100644 index 0000000000..005230ed3f --- /dev/null +++ b/source/java/org/alfresco/jcr/example/wiki-context.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + org/alfresco/jcr/example/wikiModel.xml + + + + + diff --git a/source/java/org/alfresco/jcr/example/wikiImage.gif b/source/java/org/alfresco/jcr/example/wikiImage.gif new file mode 100644 index 0000000000..a3e1ca59bd Binary files /dev/null and b/source/java/org/alfresco/jcr/example/wikiImage.gif differ diff --git a/source/java/org/alfresco/jcr/example/wikiModel.xml b/source/java/org/alfresco/jcr/example/wikiModel.xml new file mode 100644 index 0000000000..1b0642a7b5 --- /dev/null +++ b/source/java/org/alfresco/jcr/example/wikiModel.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + WIKI Page + cm:content + + + d:boolean + false + + + d:text + true + + + + cm:titled + + + + + + diff --git a/source/java/org/alfresco/jcr/exporter/JCRDocumentXMLExporter.java b/source/java/org/alfresco/jcr/exporter/JCRDocumentXMLExporter.java new file mode 100644 index 0000000000..d33715f041 --- /dev/null +++ b/source/java/org/alfresco/jcr/exporter/JCRDocumentXMLExporter.java @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.exporter; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.jcr.RepositoryException; +import javax.jcr.Value; +import javax.jcr.ValueFormatException; + +import org.alfresco.jcr.dictionary.JCRNamespace; +import org.alfresco.jcr.item.NodeImpl; +import org.alfresco.jcr.item.PropertyImpl; +import org.alfresco.jcr.item.property.JCRMixinTypesProperty; +import org.alfresco.jcr.item.property.JCRPrimaryTypeProperty; +import org.alfresco.jcr.item.property.JCRUUIDProperty; +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.cmr.security.AccessPermission; +import org.alfresco.service.cmr.view.Exporter; +import org.alfresco.service.cmr.view.ExporterContext; +import org.alfresco.service.cmr.view.ExporterException; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Base64; +import org.alfresco.util.ISO9075; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + + +/** + * Alfresco Implementation of JCR Document XML Exporter + * + * @author David Caruana + */ +public class JCRDocumentXMLExporter implements Exporter +{ + + private SessionImpl session; + private ContentHandler contentHandler; + private List currentProperties = new ArrayList(); + private List currentValues = new ArrayList(); + + + /** + * Construct + * + * @param namespaceService namespace service + * @param nodeService node service + * @param contentHandler content handler + */ + public JCRDocumentXMLExporter(SessionImpl session, ContentHandler contentHandler) + { + this.session = session; + this.contentHandler = contentHandler; + } + + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#start() + */ + public void start(ExporterContext exportNodeRef) + { + try + { + contentHandler.startDocument(); + } + catch (SAXException e) + { + throw new ExporterException("Failed to process export start event", e); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startNamespace(java.lang.String, java.lang.String) + */ + public void startNamespace(String prefix, String uri) + { + try + { + contentHandler.startPrefixMapping(prefix, uri); + } + catch (SAXException e) + { + throw new ExporterException("Failed to process start namespace event - prefix " + prefix + " uri " + uri, e); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endNamespace(java.lang.String) + */ + public void endNamespace(String prefix) + { + try + { + contentHandler.endPrefixMapping(prefix); + } + catch (SAXException e) + { + throw new ExporterException("Failed to process end namespace event - prefix " + prefix, e); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startNode(org.alfresco.service.cmr.repository.NodeRef) + */ + public void startNode(NodeRef nodeRef) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endNode(org.alfresco.service.cmr.repository.NodeRef) + */ + public void endNode(NodeRef nodeRef) + { + try + { + QName nodeName = getNodeName(nodeRef); + contentHandler.endElement(nodeName.getNamespaceURI(), nodeName.getLocalName(), toPrefixString(nodeName)); + } + catch (SAXException e) + { + throw new ExporterException("Failed to process end node event - node ref " + nodeRef.toString(), e); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startAspects(org.alfresco.service.cmr.repository.NodeRef) + */ + public void startAspects(NodeRef nodeRef) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endAspects(org.alfresco.service.cmr.repository.NodeRef) + */ + public void endAspects(NodeRef nodeRef) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void startAspect(NodeRef nodeRef, QName aspect) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void endAspect(NodeRef nodeRef, QName aspect) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startACL(org.alfresco.service.cmr.repository.NodeRef) + */ + public void startACL(NodeRef nodeRef) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#permission(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.security.AccessPermission) + */ + public void permission(NodeRef nodeRef, AccessPermission permission) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endACL(org.alfresco.service.cmr.repository.NodeRef) + */ + public void endACL(NodeRef nodeRef) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startProperties(org.alfresco.service.cmr.repository.NodeRef) + */ + public void startProperties(NodeRef nodeRef) + { + currentProperties.clear(); + currentValues.clear(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endProperties(org.alfresco.service.cmr.repository.NodeRef) + */ + public void endProperties(NodeRef nodeRef) + { + try + { + // create node attributes + AttributesImpl attrs = new AttributesImpl(); + + // primary type + NodeImpl nodeImpl = new NodeImpl(session, nodeRef); + PropertyImpl primaryType = new JCRPrimaryTypeProperty(nodeImpl); + attrs.addAttribute(JCRPrimaryTypeProperty.PROPERTY_NAME.getNamespaceURI(), JCRPrimaryTypeProperty.PROPERTY_NAME.getLocalName(), + toPrefixString(JCRPrimaryTypeProperty.PROPERTY_NAME), null, getValue(primaryType.getValue().getString())); + + // mixin type + PropertyImpl mixinTypes = new JCRMixinTypesProperty(nodeImpl); + Collection mixins = new ArrayList(); + for (Value value : mixinTypes.getValues()) + { + mixins.add(value.getString()); + } + attrs.addAttribute(JCRMixinTypesProperty.PROPERTY_NAME.getNamespaceURI(), JCRMixinTypesProperty.PROPERTY_NAME.getLocalName(), + toPrefixString(JCRMixinTypesProperty.PROPERTY_NAME), null, getCollectionValue(mixins)); + + // uuid (for mix:referencable) + attrs.addAttribute(JCRUUIDProperty.PROPERTY_NAME.getNamespaceURI(), JCRUUIDProperty.PROPERTY_NAME.getLocalName(), + toPrefixString(JCRUUIDProperty.PROPERTY_NAME), null, getValue(nodeRef.getId())); + + // node properties + for (int i = 0; i < currentProperties.size(); i++) + { + Object value = currentValues.get(i); + String strValue = (value instanceof Collection) ? getCollectionValue((Collection)value) : getValue(value); + QName propName = currentProperties.get(i); + propName = encodeQName(propName); + attrs.addAttribute(propName.getNamespaceURI(), propName.getLocalName(), toPrefixString(propName), null, strValue); + } + + // emit node element + QName nodeName = getNodeName(nodeRef); + contentHandler.startElement(nodeName.getNamespaceURI(), nodeName.getLocalName(), toPrefixString(nodeName), attrs); + } + catch (ValueFormatException e) + { + throw new ExporterException("Failed to process properties event - nodeRef " + nodeRef); + } + catch (RepositoryException e) + { + throw new ExporterException("Failed to process properties event - nodeRef " + nodeRef); + } + catch (SAXException e) + { + throw new ExporterException("Failed to process properties event - nodeRef " + nodeRef); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startProperty(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void startProperty(NodeRef nodeRef, QName property) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endProperty(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void endProperty(NodeRef nodeRef, QName property) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startValueCollection(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void startValueCollection(NodeRef nodeRef, QName property) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endValueCollection(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void endValueCollection(NodeRef nodeRef, QName property) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#value(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, java.io.Serializable) + */ + public void value(NodeRef nodeRef, QName property, Object value, int index) + { + currentProperties.add(property); + currentValues.add(value); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#content(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, java.io.InputStream) + */ + public void content(NodeRef nodeRef, QName property, InputStream content, ContentData contentData, int index) + { + try + { + StringBuffer strValue = new StringBuffer(9 * 1024); + if (content != null) + { + // emit base64 encoded content + InputStream base64content = new Base64.InputStream(content, Base64.ENCODE | Base64.DONT_BREAK_LINES); + byte[] buffer = new byte[9 * 1024]; + int read; + while ((read = base64content.read(buffer, 0, buffer.length)) > 0) + { + String characters = new String(buffer, 0, read); + strValue.append(characters); + } + } + currentProperties.add(property); + currentValues.add(strValue.toString()); + } + catch (IOException e) + { + throw new ExporterException("Failed to process content event - nodeRef " + nodeRef + "; property " + toPrefixString(property)); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startAssoc(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void startAssoc(NodeRef nodeRef, QName assoc) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endAssoc(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void endAssoc(NodeRef nodeRef, QName assoc) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startAssocs(org.alfresco.service.cmr.repository.NodeRef) + */ + public void startAssocs(NodeRef nodeRef) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endAssocs(org.alfresco.service.cmr.repository.NodeRef) + */ + public void endAssocs(NodeRef nodeRef) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startReference(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void startReference(NodeRef nodeRef, QName childName) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endReference(org.alfresco.service.cmr.repository.NodeRef) + */ + public void endReference(NodeRef nodeRef) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#warning(java.lang.String) + */ + public void warning(String warning) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#end() + */ + public void end() + { + try + { + contentHandler.endDocument(); + } + catch (SAXException e) + { + throw new ExporterException("Failed to process end export event", e); + } + } + + /** + * Get the prefix for the specified URI + * @param uri the URI + * @return the prefix (or null, if one is not registered) + */ + private String toPrefixString(QName qname) + { + return qname.toPrefixString(session.getNamespaceResolver()); + } + + /** + * Get name of Node + * + * @param nodeRef node reference + * @return node name + */ + private QName getNodeName(NodeRef nodeRef) + { + // establish name of node + QName childQName = null; + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + NodeRef rootNode = nodeService.getRootNode(nodeRef.getStoreRef()); + if (rootNode.equals(nodeRef)) + { + childQName = QName.createQName(JCRNamespace.JCR_URI, "root"); + } + else + { + Path path = nodeService.getPath(nodeRef); + String childName = path.last().getElementString(); + childQName = QName.createQName(childName); + childQName = encodeQName(childQName); + } + + return childQName; + } + + /** + * Get single-valued property + * + * @param value + * @return + */ + private String getValue(Object value) + throws RepositoryException + { + String strValue = session.getTypeConverter().convert(String.class, value); + return encodeBlanks(strValue); + } + + + /** + * Get multi-valued property + * + * @param values + * @return + */ + private String getCollectionValue(Collection values) + { + Collection strValues = session.getTypeConverter().getConverter().convert(String.class, values); + StringBuffer buffer = new StringBuffer(); + int i = 0; + for (String strValue : strValues) + { + buffer.append(encodeBlanks(strValue)); + i++; + if (i < strValues.size()) + { + buffer.append(" "); + } + } + return buffer.toString(); + } + + /** + * Encode Name for Document View Output + * + * @param name name to encode + * @return encoded name + */ + private QName encodeQName(QName name) + { + return QName.createQName(name.getNamespaceURI(), ISO9075.encode(name.getLocalName())); + } + + /** + * Encode blanks in value + * + * @param value + * @return + */ + private String encodeBlanks(String value) + { + return value.replaceAll(" ", "_x0020_"); + } + + +} diff --git a/source/java/org/alfresco/jcr/exporter/JCRSystemXMLExporter.java b/source/java/org/alfresco/jcr/exporter/JCRSystemXMLExporter.java new file mode 100644 index 0000000000..a9d3b3e9bf --- /dev/null +++ b/source/java/org/alfresco/jcr/exporter/JCRSystemXMLExporter.java @@ -0,0 +1,464 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.exporter; + +import java.io.IOException; +import java.io.InputStream; + +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; + +import org.alfresco.jcr.dictionary.JCRNamespace; +import org.alfresco.jcr.dictionary.PropertyDefinitionImpl; +import org.alfresco.jcr.item.NodeImpl; +import org.alfresco.jcr.item.PropertyImpl; +import org.alfresco.jcr.item.property.JCRMixinTypesProperty; +import org.alfresco.jcr.item.property.JCRPrimaryTypeProperty; +import org.alfresco.jcr.item.property.JCRUUIDProperty; +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.cmr.security.AccessPermission; +import org.alfresco.service.cmr.view.Exporter; +import org.alfresco.service.cmr.view.ExporterContext; +import org.alfresco.service.cmr.view.ExporterException; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Base64; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + + +/** + * Alfresco Implementation of JCR System XML Exporter + * + * @author David Caruana + */ +public class JCRSystemXMLExporter implements Exporter +{ + public final static String NODE_LOCALNAME = "node"; + public final static String NAME_LOCALNAME = "name"; + public final static String PROPERTY_LOCALNAME = "property"; + public final static String TYPE_LOCALNAME = "type"; + public final static String VALUE_LOCALNAME = "value"; + + public final static QName NODE_QNAME = QName.createQName(JCRNamespace.SV_URI, NODE_LOCALNAME); + public final static QName NAME_QNAME = QName.createQName(JCRNamespace.SV_URI, NAME_LOCALNAME); + public final static QName PROPERTY_QNAME = QName.createQName(JCRNamespace.SV_URI, PROPERTY_LOCALNAME); + public final static QName TYPE_QNAME = QName.createQName(JCRNamespace.SV_URI, TYPE_LOCALNAME); + public final static QName VALUE_QNAME = QName.createQName(JCRNamespace.SV_URI, VALUE_LOCALNAME); + + private SessionImpl session; + private ContentHandler contentHandler; + + private final static AttributesImpl EMPTY_ATTRIBUTES = new AttributesImpl(); + + + /** + * Construct + * + * @param namespaceService namespace service + * @param nodeService node service + * @param contentHandler content handler + */ + public JCRSystemXMLExporter(SessionImpl session, ContentHandler contentHandler) + { + this.session = session; + this.contentHandler = contentHandler; + } + + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#start() + */ + public void start(ExporterContext exportNodeRef) + { + try + { + contentHandler.startDocument(); + } + catch (SAXException e) + { + throw new ExporterException("Failed to process export start event", e); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startNamespace(java.lang.String, java.lang.String) + */ + public void startNamespace(String prefix, String uri) + { + try + { + contentHandler.startPrefixMapping(prefix, uri); + } + catch (SAXException e) + { + throw new ExporterException("Failed to process start namespace event - prefix " + prefix + " uri " + uri, e); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endNamespace(java.lang.String) + */ + public void endNamespace(String prefix) + { + try + { + contentHandler.endPrefixMapping(prefix); + } + catch (SAXException e) + { + throw new ExporterException("Failed to process end namespace event - prefix " + prefix, e); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startNode(org.alfresco.service.cmr.repository.NodeRef) + */ + public void startNode(NodeRef nodeRef) + { + try + { + // establish name of node + String childName; + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + NodeRef rootNode = nodeService.getRootNode(nodeRef.getStoreRef()); + if (rootNode.equals(nodeRef)) + { + childName = "jcr:root"; + } + else + { + Path path = nodeService.getPath(nodeRef); + childName = path.last().getElementString(); + } + QName childQName = QName.createQName(childName); + + // create jcr node attributes + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute(NAME_QNAME.getNamespaceURI(), NAME_LOCALNAME, toPrefixString(NAME_QNAME), null, toPrefixString(childQName)); + + // emit node element + contentHandler.startElement(NODE_QNAME.getNamespaceURI(), NODE_LOCALNAME, toPrefixString(NODE_QNAME), attrs); + + // + // emit jcr specifics + // + NodeImpl nodeImpl = new NodeImpl(session, nodeRef); + + // primary type + PropertyImpl primaryType = new JCRPrimaryTypeProperty(nodeImpl); + startProperty(nodeRef, JCRPrimaryTypeProperty.PROPERTY_NAME); + value(nodeRef, JCRPrimaryTypeProperty.PROPERTY_NAME, primaryType.getValue().getString(), -1); + endProperty(nodeRef, JCRPrimaryTypeProperty.PROPERTY_NAME); + + // mixin type + PropertyImpl mixinTypes = new JCRMixinTypesProperty(nodeImpl); + startProperty(nodeRef, JCRMixinTypesProperty.PROPERTY_NAME); + Value[] mixinValues = mixinTypes.getValues(); + for (int i = 0; i < mixinValues.length; i++) + { + value(nodeRef, JCRMixinTypesProperty.PROPERTY_NAME, mixinValues[i], i); + } + endProperty(nodeRef, JCRMixinTypesProperty.PROPERTY_NAME); + + // uuid (for mix:referencable) + startProperty(nodeRef, JCRUUIDProperty.PROPERTY_NAME); + value(nodeRef, JCRUUIDProperty.PROPERTY_NAME, nodeRef.getId(), -1); + endProperty(nodeRef, JCRUUIDProperty.PROPERTY_NAME); + } + catch (SAXException e) + { + throw new ExporterException("Failed to process start node event - node ref " + nodeRef.toString(), e); + } + catch (RepositoryException e) + { + throw new ExporterException("Failed to process start node event - node ref " + nodeRef.toString(), e); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endNode(org.alfresco.service.cmr.repository.NodeRef) + */ + public void endNode(NodeRef nodeRef) + { + try + { + contentHandler.endElement(NODE_QNAME.getNamespaceURI(), NODE_LOCALNAME, toPrefixString(NODE_QNAME)); + } + catch (SAXException e) + { + throw new ExporterException("Failed to process end node event - node ref " + nodeRef.toString(), e); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startAspects(org.alfresco.service.cmr.repository.NodeRef) + */ + public void startAspects(NodeRef nodeRef) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endAspects(org.alfresco.service.cmr.repository.NodeRef) + */ + public void endAspects(NodeRef nodeRef) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void startAspect(NodeRef nodeRef, QName aspect) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void endAspect(NodeRef nodeRef, QName aspect) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startACL(org.alfresco.service.cmr.repository.NodeRef) + */ + public void startACL(NodeRef nodeRef) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#permission(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.security.AccessPermission) + */ + public void permission(NodeRef nodeRef, AccessPermission permission) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endACL(org.alfresco.service.cmr.repository.NodeRef) + */ + public void endACL(NodeRef nodeRef) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startProperties(org.alfresco.service.cmr.repository.NodeRef) + */ + public void startProperties(NodeRef nodeRef) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endProperties(org.alfresco.service.cmr.repository.NodeRef) + */ + public void endProperties(NodeRef nodeRef) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startProperty(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void startProperty(NodeRef nodeRef, QName property) + { + try + { + // create jcr node attributes + DictionaryService dictionaryService = session.getRepositoryImpl().getServiceRegistry().getDictionaryService(); + PropertyDefinition propDef = dictionaryService.getProperty(property); + PropertyDefinitionImpl propDefImpl = new PropertyDefinitionImpl(session.getTypeManager(), propDef); + String datatype = PropertyType.nameFromValue(propDefImpl.getRequiredType()); + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute(NAME_QNAME.getNamespaceURI(), NAME_LOCALNAME, toPrefixString(NAME_QNAME), null, toPrefixString(property)); + attrs.addAttribute(TYPE_QNAME.getNamespaceURI(), TYPE_LOCALNAME, toPrefixString(TYPE_QNAME), null, datatype); + + // emit property element + contentHandler.startElement(PROPERTY_QNAME.getNamespaceURI(), PROPERTY_LOCALNAME, toPrefixString(PROPERTY_QNAME), attrs); + } + catch (SAXException e) + { + throw new ExporterException("Failed to process start property event - nodeRef " + nodeRef + "; property " + toPrefixString(property), e); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endProperty(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void endProperty(NodeRef nodeRef, QName property) + { + try + { + // emit property element + contentHandler.endElement(PROPERTY_QNAME.getNamespaceURI(), PROPERTY_LOCALNAME, toPrefixString(PROPERTY_QNAME)); + } + catch (SAXException e) + { + throw new ExporterException("Failed to process end property event - nodeRef " + nodeRef + "; property " + toPrefixString(property), e); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startValueCollection(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void startValueCollection(NodeRef nodeRef, QName property) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endValueCollection(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void endValueCollection(NodeRef nodeRef, QName property) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#value(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, java.io.Serializable) + */ + public void value(NodeRef nodeRef, QName property, Object value, int index) + { + try + { + // emit value element + contentHandler.startElement(VALUE_QNAME.getNamespaceURI(), VALUE_LOCALNAME, toPrefixString(VALUE_QNAME), EMPTY_ATTRIBUTES); + String strValue = session.getTypeConverter().convert(String.class, value); + contentHandler.characters(strValue.toCharArray(), 0, strValue.length()); + contentHandler.endElement(VALUE_QNAME.getNamespaceURI(), VALUE_LOCALNAME, toPrefixString(VALUE_QNAME)); + } + catch (RepositoryException e) + { + throw new ExporterException("Failed to process value event - nodeRef " + nodeRef + "; property " + toPrefixString(property) + "; value " + value, e); + } + catch (SAXException e) + { + throw new ExporterException("Failed to process value event - nodeRef " + nodeRef + "; property " + toPrefixString(property) + "; value " + value, e); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#content(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, java.io.InputStream) + */ + public void content(NodeRef nodeRef, QName property, InputStream content, ContentData contentData, int index) + { + try + { + contentHandler.startElement(VALUE_QNAME.getNamespaceURI(), VALUE_LOCALNAME, toPrefixString(VALUE_QNAME), EMPTY_ATTRIBUTES); + + if (content != null) + { + // emit base64 encoded content + InputStream base64content = new Base64.InputStream(content, Base64.ENCODE | Base64.DONT_BREAK_LINES); + byte[] buffer = new byte[9 * 1024]; + int read; + while ((read = base64content.read(buffer, 0, buffer.length)) > 0) + { + String characters = new String(buffer, 0, read); + contentHandler.characters(characters.toCharArray(), 0, characters.length()); + } + } + + contentHandler.endElement(VALUE_QNAME.getNamespaceURI(), VALUE_LOCALNAME, toPrefixString(VALUE_QNAME)); + } + catch (SAXException e) + { + throw new ExporterException("Failed to process content event - nodeRef " + nodeRef + "; property " + toPrefixString(property)); + } + catch (IOException e) + { + throw new ExporterException("Failed to process content event - nodeRef " + nodeRef + "; property " + toPrefixString(property)); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startAssoc(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void startAssoc(NodeRef nodeRef, QName assoc) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endAssoc(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void endAssoc(NodeRef nodeRef, QName assoc) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startAssocs(org.alfresco.service.cmr.repository.NodeRef) + */ + public void startAssocs(NodeRef nodeRef) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endAssocs(org.alfresco.service.cmr.repository.NodeRef) + */ + public void endAssocs(NodeRef nodeRef) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#startReference(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void startReference(NodeRef nodeRef, QName childName) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#endReference(org.alfresco.service.cmr.repository.NodeRef) + */ + public void endReference(NodeRef nodeRef) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#warning(java.lang.String) + */ + public void warning(String warning) + { + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.view.Exporter#end() + */ + public void end() + { + try + { + contentHandler.endDocument(); + } + catch (SAXException e) + { + throw new ExporterException("Failed to process end export event", e); + } + } + + /** + * Get the prefix for the specified URI + * @param uri the URI + * @return the prefix (or null, if one is not registered) + */ + private String toPrefixString(QName qname) + { + return qname.toPrefixString(session.getNamespaceResolver()); + } + +} diff --git a/source/java/org/alfresco/jcr/importer/ImportTest.java b/source/java/org/alfresco/jcr/importer/ImportTest.java new file mode 100644 index 0000000000..798f3db332 --- /dev/null +++ b/source/java/org/alfresco/jcr/importer/ImportTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.importer; + +import javax.jcr.ImportUUIDBehavior; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; + +import org.alfresco.jcr.test.BaseJCRTest; +import org.springframework.core.io.ClassPathResource; + + +public class ImportTest extends BaseJCRTest +{ + protected Session superuserSession; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + SimpleCredentials superuser = new SimpleCredentials("superuser", "".toCharArray()); + superuserSession = repository.login(superuser, getWorkspace()); + } + + @Override + protected void tearDown() throws Exception + { + super.tearDown(); + superuserSession.logout(); + } + + public void testSysImport() + throws Exception + { + ClassPathResource sysview = new ClassPathResource("org/alfresco/jcr/test/sysview.xml"); + superuserSession.importXML("/testroot", sysview.getInputStream(), ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW); + } + + public void testDocImport() + throws Exception + { + ClassPathResource sysview = new ClassPathResource("org/alfresco/jcr/test/docview.xml"); + superuserSession.importXML("/testroot", sysview.getInputStream(), ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW); + } + + public void testThrowCollision() + throws Exception + { + ClassPathResource sysview = new ClassPathResource("org/alfresco/jcr/test/docview.xml"); + superuserSession.importXML("/testroot", sysview.getInputStream(), ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING); + + try + { + superuserSession.importXML("/testroot", sysview.getInputStream(), ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW); + fail("Failed to catch UUID collision"); + } + catch(RepositoryException e) + { + } + } + +} diff --git a/source/java/org/alfresco/jcr/importer/JCRDocXMLHandler.java b/source/java/org/alfresco/jcr/importer/JCRDocXMLHandler.java new file mode 100644 index 0000000000..492b54f9c6 --- /dev/null +++ b/source/java/org/alfresco/jcr/importer/JCRDocXMLHandler.java @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.importer; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.Serializable; +import java.util.List; +import java.util.Stack; + +import javax.jcr.InvalidSerializedDataException; + +import org.alfresco.jcr.dictionary.JCRNamespace; +import org.alfresco.jcr.item.property.JCRMixinTypesProperty; +import org.alfresco.jcr.item.property.JCRPrimaryTypeProperty; +import org.alfresco.jcr.item.property.JCRUUIDProperty; +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.repo.importer.ImportContentHandler; +import org.alfresco.repo.importer.Importer; +import org.alfresco.repo.importer.view.ElementContext; +import org.alfresco.repo.importer.view.NodeContext; +import org.alfresco.repo.importer.view.ParentContext; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.InvalidTypeException; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.view.ImporterException; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Base64; +import org.alfresco.util.ISO9075; +import org.xml.sax.Attributes; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + + +/** + * Alfresco implementation of a Doc View Import Content Handler + * + * @author David Caruana + */ +public class JCRDocXMLHandler implements ImportContentHandler +{ + private Importer importer; + private SessionImpl session; + private DictionaryService dictionaryService; + private NamespacePrefixResolver importResolver; + private Stack contextStack = new Stack(); + + + /** + * Construct + * + * @param session JCR Session + * @param importResolver Namespace Resolver for the Import + */ + public JCRDocXMLHandler(SessionImpl session, NamespacePrefixResolver importResolver) + { + this.session = session; + this.importResolver = importResolver; + this.dictionaryService = session.getRepositoryImpl().getServiceRegistry().getDictionaryService(); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.importer.ImportContentHandler#setImporter(org.alfresco.repo.importer.Importer) + */ + public void setImporter(Importer importer) + { + this.importer = importer; + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.importer.ImportContentHandler#importStream(java.lang.String) + */ + public InputStream importStream(String content) + { + File contentFile = new File(content); + try + { + FileInputStream contentStream = new FileInputStream(contentFile); + return new Base64.InputStream(contentStream, Base64.DECODE | Base64.DONT_BREAK_LINES); + } + catch (FileNotFoundException e) + { + throw new ImporterException("Failed to retrieve import input stream on temporary content file " + content); + } + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator) + */ + public void setDocumentLocator(Locator locator) + { + // NOOP + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#startDocument() + */ + public void startDocument() throws SAXException + { + // NOOP + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#endDocument() + */ + public void endDocument() throws SAXException + { + // NOOP + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, java.lang.String) + */ + public void startPrefixMapping(String prefix, String uri) throws SAXException + { + // NOOP + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String) + */ + public void endPrefixMapping(String prefix) throws SAXException + { + // NOOP + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) + */ + public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException + { + try + { + // construct qname for element + QName elementName = decodeQName(QName.createQName(qName, importResolver)); + + // setup parent context + ParentContext parentContext = null; + if (contextStack.empty()) + { + // create root parent context + parentContext = new ParentContext(elementName, dictionaryService, importer); + } + else + { + // create parent context + NodeContext parentNode = (NodeContext)contextStack.peek(); + parentContext = new ParentContext(elementName, parentNode); + } + + // create node context + NodeContext node = new NodeContext(elementName, parentContext, null); + node.setChildName(elementName.toPrefixString(importResolver)); + contextStack.push(node); + + // process node properties + for (int i = 0; i < atts.getLength(); i++) + { + QName propName = decodeQName(QName.createQName(atts.getURI(i), atts.getLocalName(i))); + String value = atts.getValue(i); + + // + // process "well-known" properties + // + + if (propName.equals(JCRPrimaryTypeProperty.PROPERTY_NAME)) + { + // primary type + QName primaryTypeQName = QName.createQName(value, importResolver); + TypeDefinition typeDef = dictionaryService.getType(primaryTypeQName); + if (typeDef == null) + { + throw new InvalidTypeException(primaryTypeQName); + } + node.setTypeDefinition(typeDef); + } + else if (propName.equals(JCRMixinTypesProperty.PROPERTY_NAME)) + { + // aspects + String[] aspects = value.split(" "); + for (String aspect : aspects) + { + // ignore JCR specific aspects + QName aspectQName = QName.createQName(aspect, importResolver); + if (!(JCRNamespace.JCR_URI.equals(aspectQName.getNamespaceURI()) || + JCRNamespace.MIX_URI.equals(aspectQName.getNamespaceURI()))) + { + AspectDefinition aspectDef = dictionaryService.getAspect(aspectQName); + if (aspectDef == null) + { + throw new InvalidTypeException(aspectQName); + } + node.addAspect(aspectDef); + } + } + } + else if (JCRUUIDProperty.PROPERTY_NAME.equals(propName)) + { + node.setUUID(value); + } + + // + // Note: ignore JCR specific properties + // + + else if (JCRNamespace.JCR_URI.equals(propName.getNamespaceURI())) + { + } + + // + // process all other properties + // + + else + { + // determine type of property + PropertyDefinition propDef = dictionaryService.getProperty(propName); + if (propDef == null) + { + throw new ImporterException("Property " + propName + " is not known to the repository data dictionary"); + } + DataTypeDefinition dataTypeDef = propDef.getDataType(); + + // extract values from node xml attribute + String[] propValues = null; + PropertyContext propertyContext = new PropertyContext(elementName, node, propName, dataTypeDef.getName()); + if (dataTypeDef.getName().equals(DataTypeDefinition.CONTENT)) + { + // Note: we only support single valued content properties + propValues = new String[] { value }; + } + else + { + // attempt to split multi-value properties + propValues = value.split(" "); + } + + // extract values appropriately + for (String propValue : propValues) + { + propertyContext.startValue(); + propertyContext.appendCharacters(propValue.toCharArray(), 0, propValue.length()); + propertyContext.endValue(); + } + + // add each value to the node + if (propertyContext.isMultiValue()) + { + node.addPropertyCollection(propName); + } + List nodeValues = propertyContext.getValues(); + for (StringBuffer nodeValue : nodeValues) + { + // first, cast value to appropriate type (using JCR converters) + Serializable objVal = (Serializable)session.getTypeConverter().convert(dataTypeDef, nodeValue.toString()); + String strValue = DefaultTypeConverter.INSTANCE.convert(String.class, objVal); + node.addProperty(propName, strValue); + } + } + } + + // import node + NodeRef nodeRef = node.getImporter().importNode(node); + node.setNodeRef(nodeRef); + } + catch(Exception e) + { + throw new SAXException("Failed to process element " + qName, e); + } + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String) + */ + public void endElement(String uri, String localName, String qName) throws SAXException + { + try + { + // ensure context matches parse + ElementContext context = (ElementContext)contextStack.pop(); + QName elementName = QName.createQName(qName, importResolver); + if (!context.getElementName().equals(elementName)) + { + throw new InvalidSerializedDataException("Expected element " + context.getElementName() + " but was " + elementName); + } + + // signal end of node + NodeContext nodeContext = (NodeContext)context; + nodeContext.getImporter().childrenImported(nodeContext.getNodeRef()); + } + catch(Exception e) + { + throw new SAXException("Failed to process element " + qName, e); + } + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#characters(char[], int, int) + */ + public void characters(char[] ch, int start, int length) throws SAXException + { + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int) + */ + public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException + { + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, java.lang.String) + */ + public void processingInstruction(String target, String data) throws SAXException + { + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String) + */ + public void skippedEntity(String name) throws SAXException + { + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException) + */ + public void warning(SAXParseException exception) throws SAXException + { + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException) + */ + public void error(SAXParseException exception) throws SAXException + { + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException) + */ + public void fatalError(SAXParseException exception) throws SAXException + { + } + + /** + * Decode QName + * + * @param name name to decode + * @return the decoded name + */ + private QName decodeQName(QName name) + { + return QName.createQName(name.getNamespaceURI(), ISO9075.decode(name.getLocalName())); + } + +} diff --git a/source/java/org/alfresco/jcr/importer/JCRImportHandler.java b/source/java/org/alfresco/jcr/importer/JCRImportHandler.java new file mode 100644 index 0000000000..9379b2f163 --- /dev/null +++ b/source/java/org/alfresco/jcr/importer/JCRImportHandler.java @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.importer; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.alfresco.jcr.dictionary.JCRNamespace; +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.repo.importer.ImportContentHandler; +import org.alfresco.repo.importer.Importer; +import org.alfresco.service.cmr.view.ImporterException; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.NamespaceService; +import org.xml.sax.Attributes; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.NamespaceSupport; + + +/** + * Import Handler that is sensitive to Document and System View XML schemas. + * + * @author David Caruana + */ +public class JCRImportHandler implements ImportContentHandler +{ + private Importer importer; + private SessionImpl session; + private NamespaceContext namespaceContext; + private ImportContentHandler targetHandler = null; + + + /** + * Construct + * + * @param session + */ + public JCRImportHandler(SessionImpl session) + { + this.session = session; + this.namespaceContext = new NamespaceContext(); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.importer.ImportContentHandler#setImporter(org.alfresco.repo.importer.Importer) + */ + public void setImporter(Importer importer) + { + this.importer = importer; + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.importer.ImportContentHandler#importStream(java.lang.String) + */ + public InputStream importStream(String content) + { + return targetHandler.importStream(content); + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator) + */ + public void setDocumentLocator(Locator locator) + { + // NOOP + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#startDocument() + */ + public void startDocument() throws SAXException + { + namespaceContext.reset(); + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#endDocument() + */ + public void endDocument() throws SAXException + { + targetHandler.endDocument(); + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, java.lang.String) + */ + public void startPrefixMapping(String prefix, String uri) throws SAXException + { + // ensure uri has been registered + NamespacePrefixResolver resolver = session.getNamespaceResolver(); + Collection uris = resolver.getURIs(); + if (!uris.contains(uri)) + { + throw new ImporterException("Namespace URI " + uri + " has not been registered with the repository"); + } + + // register prefix within this namespace context + namespaceContext.registerPrefix(prefix, uri); + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String) + */ + public void endPrefixMapping(String prefix) throws SAXException + { + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) + */ + public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException + { + namespaceContext.pushContext(); + + // determine content handler based on first element of document + if (targetHandler == null) + { + if (JCRNamespace.SV_URI.equals(uri)) + { + targetHandler = new JCRSystemXMLHandler(session, namespaceContext); + } + else + { + targetHandler = new JCRDocXMLHandler(session, namespaceContext); + } + targetHandler.setImporter(importer); + targetHandler.startDocument(); + } + + targetHandler.startElement(uri, localName, qName, atts); + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String) + */ + public void endElement(String uri, String localName, String qName) throws SAXException + { + targetHandler.endElement(uri, localName, qName); + namespaceContext.popContext(); + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#characters(char[], int, int) + */ + public void characters(char[] ch, int start, int length) throws SAXException + { + targetHandler.characters(ch, start, length); + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int) + */ + public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException + { + targetHandler.characters(ch, start, length); + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, java.lang.String) + */ + public void processingInstruction(String target, String data) throws SAXException + { + targetHandler.processingInstruction(target, data); + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String) + */ + public void skippedEntity(String name) throws SAXException + { + targetHandler.skippedEntity(name); + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException) + */ + public void warning(SAXParseException exception) throws SAXException + { + targetHandler.warning(exception); + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException) + */ + public void error(SAXParseException exception) throws SAXException + { + targetHandler.error(exception); + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException) + */ + public void fatalError(SAXParseException exception) throws SAXException + { + targetHandler.fatalError(exception); + } + + + /** + * Namespace Context + * + * Implementation supported by NamespaceSupport which itself does not + * handle empty uri registration. + */ + private static class NamespaceContext implements NamespacePrefixResolver + { + private final NamespaceSupport context; + private static final String REMAPPED_DEFAULT_URI = " "; + + + /** + * Construct + */ + private NamespaceContext() + { + context = new NamespaceSupport(); + } + + /** + * Clear namespace declarations + */ + private void reset() + { + context.reset(); + } + + /** + * Push a new Namespace Context + */ + private void pushContext() + { + context.pushContext(); + } + + /** + * Pop a Namespace Context + */ + private void popContext() + { + context.popContext(); + } + + /** + * Register a namespace prefix + * + * @param prefix + * @param uri + * @return true => legal prefix; false => illegal prefix + */ + private boolean registerPrefix(String prefix, String uri) + { + if (NamespaceService.DEFAULT_URI.equals(uri)) + { + uri = REMAPPED_DEFAULT_URI; + } + return context.declarePrefix(prefix, uri); + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.namespace.NamespacePrefixResolver#getNamespaceURI(java.lang.String) + */ + public String getNamespaceURI(String prefix) throws org.alfresco.service.namespace.NamespaceException + { + String uri = context.getURI(prefix); + if (uri == null) + { + throw new org.alfresco.service.namespace.NamespaceException("Namespace prefix " + prefix + " not registered."); + } + if (REMAPPED_DEFAULT_URI.equals(uri)) + { + return NamespaceService.DEFAULT_URI; + } + return uri; + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.namespace.NamespacePrefixResolver#getPrefixes(java.lang.String) + */ + public Collection getPrefixes(String namespaceURI) throws org.alfresco.service.namespace.NamespaceException + { + if (NamespaceService.DEFAULT_URI.equals(namespaceURI)) + { + namespaceURI = REMAPPED_DEFAULT_URI; + } + String prefix = context.getPrefix(namespaceURI); + if (prefix == null) + { + if (namespaceURI.equals(context.getURI(NamespaceService.DEFAULT_PREFIX))) + { + prefix = NamespaceService.DEFAULT_PREFIX; + } + else + { + throw new org.alfresco.service.namespace.NamespaceException("Namespace URI " + namespaceURI + " not registered."); + } + } + List prefixes = new ArrayList(1); + prefixes.add(prefix); + return prefixes; + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.namespace.NamespacePrefixResolver#getPrefixes() + */ + public Collection getPrefixes() + { + // NOTE: not required in this context + return null; + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.namespace.NamespacePrefixResolver#getURIs() + */ + public Collection getURIs() + { + // NOTE: not required in this context + return null; + } + } + +} diff --git a/source/java/org/alfresco/jcr/importer/JCRSystemXMLHandler.java b/source/java/org/alfresco/jcr/importer/JCRSystemXMLHandler.java new file mode 100644 index 0000000000..cf0f5a1bce --- /dev/null +++ b/source/java/org/alfresco/jcr/importer/JCRSystemXMLHandler.java @@ -0,0 +1,552 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.importer; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.Serializable; +import java.util.List; +import java.util.Stack; + +import javax.jcr.InvalidSerializedDataException; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; + +import org.alfresco.jcr.dictionary.DataTypeMap; +import org.alfresco.jcr.dictionary.JCRNamespace; +import org.alfresco.jcr.exporter.JCRSystemXMLExporter; +import org.alfresco.jcr.item.property.JCRMixinTypesProperty; +import org.alfresco.jcr.item.property.JCRPrimaryTypeProperty; +import org.alfresco.jcr.item.property.JCRUUIDProperty; +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.repo.importer.ImportContentHandler; +import org.alfresco.repo.importer.Importer; +import org.alfresco.repo.importer.view.ElementContext; +import org.alfresco.repo.importer.view.NodeContext; +import org.alfresco.repo.importer.view.ParentContext; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.InvalidTypeException; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.view.ImporterException; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Base64; +import org.xml.sax.Attributes; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + + +/** + * Alfresco implementation of a System View Import Content Handler + * + * @author David Caruana + */ +public class JCRSystemXMLHandler implements ImportContentHandler +{ + private Importer importer; + private SessionImpl session; + private NamespacePrefixResolver importResolver; + private Stack contextStack = new Stack(); + + + /** + * Construct + * + * @param session JCR Session + * @param importResolver Namespace Resolver for the Import + */ + public JCRSystemXMLHandler(SessionImpl session, NamespacePrefixResolver importResolver) + { + this.session = session; + this.importResolver = importResolver; + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.importer.ImportContentHandler#setImporter(org.alfresco.repo.importer.Importer) + */ + public void setImporter(Importer importer) + { + this.importer = importer; + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.importer.ImportContentHandler#importStream(java.lang.String) + */ + public InputStream importStream(String content) + { + File contentFile = new File(content); + try + { + FileInputStream contentStream = new FileInputStream(contentFile); + return new Base64.InputStream(contentStream, Base64.DECODE | Base64.DONT_BREAK_LINES); + } + catch (FileNotFoundException e) + { + throw new ImporterException("Failed to retrieve import input stream on temporary content file " + content); + } + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator) + */ + public void setDocumentLocator(Locator locator) + { + // NOOP + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#startDocument() + */ + public void startDocument() throws SAXException + { + // NOOP + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#endDocument() + */ + public void endDocument() throws SAXException + { + // NOOP + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, java.lang.String) + */ + public void startPrefixMapping(String prefix, String uri) throws SAXException + { + // NOOP + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String) + */ + public void endPrefixMapping(String prefix) throws SAXException + { + // NOOP + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) + */ + public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException + { + try + { + // construct qname for element + QName elementName = QName.createQName(qName, importResolver); + + if (JCRSystemXMLExporter.NODE_QNAME.equals(elementName)) + { + processStartNode(elementName, atts); + } + else if (JCRSystemXMLExporter.PROPERTY_QNAME.equals(elementName)) + { + processStartProperty(elementName, atts); + } + else if (JCRSystemXMLExporter.VALUE_QNAME.equals(elementName)) + { + processStartValue(elementName, atts); + } + } + catch(Exception e) + { + throw new SAXException("Failed to process element " + qName, e); + } + } + + /** + * Process start of Node Element + * + * @param elementName + * @param atts + * @throws InvalidSerializedDataException + */ + private void processStartNode(QName elementName, Attributes atts) + throws InvalidSerializedDataException + { + ParentContext parentContext = null; + if (contextStack.empty()) + { + // create root parent context + parentContext = new ParentContext(elementName, session.getRepositoryImpl().getServiceRegistry().getDictionaryService(), importer); + } + else + { + NodeContext parentNode = (NodeContext)contextStack.peek(); + + // if we haven't yet imported the node before its children, do so now + if (parentNode.getNodeRef() == null) + { + NodeRef nodeRef = importer.importNode(parentNode); + parentNode.setNodeRef(nodeRef); + } + + // create parent context + parentContext = new ParentContext(elementName, parentNode); + } + + // create node context + // NOTE: we don't yet know its type (we have to wait for a property) + NodeContext nodeContext = new NodeContext(elementName, parentContext, null); + + // establish node child name + String name = atts.getValue(JCRSystemXMLExporter.NAME_QNAME.toPrefixString(importResolver)); + if (name == null) + { + throw new InvalidSerializedDataException("Mandatory sv:name attribute of element sv:node not present."); + } + nodeContext.setChildName(name); + + // record new node + contextStack.push(nodeContext); + } + + /** + * Process start of Property element + * + * @param elementName + * @param atts + * @throws InvalidSerializedDataException + */ + private void processStartProperty(QName elementName, Attributes atts) + throws InvalidSerializedDataException + { + // establish correct context + ElementContext context = contextStack.peek(); + if (!(context instanceof NodeContext)) + { + throw new InvalidSerializedDataException("Element " + elementName + " not expected."); + } + NodeContext parentNode = (NodeContext)context; + + // establish property name + String name = atts.getValue(JCRSystemXMLExporter.NAME_QNAME.toPrefixString(importResolver)); + if (name == null) + { + throw new InvalidSerializedDataException("Mandatory sv:name attribute of element sv:node not present."); + } + QName propertyName = QName.createQName(name, importResolver); + + // establish property type and validate property type + QName dataType = null; + String type = atts.getValue(JCRSystemXMLExporter.TYPE_QNAME.toPrefixString(importResolver)); + if (type == null) + { + throw new InvalidSerializedDataException("Mandatory sv:type attribute of element sv:node not present."); + } + try + { + dataType = DataTypeMap.convertPropertyTypeToDataType(PropertyType.valueFromName(type)); + } + catch(IllegalArgumentException e) + { + throw new ImporterException("Type " + type + " is not known for property " + name); + } + + // construct property context + PropertyContext propertyContext = new PropertyContext(elementName, parentNode, propertyName, dataType); + contextStack.push(propertyContext); + } + + /** + * Process start of Value element + * + * @param elementName + * @param atts + * @throws InvalidSerializedDataException + */ + private void processStartValue(QName elementName, Attributes atts) + throws InvalidSerializedDataException + { + // establish correct context + ElementContext context = contextStack.peek(); + if (!(context instanceof PropertyContext)) + { + throw new InvalidSerializedDataException("Element " + elementName + " not expected."); + } + PropertyContext property = (PropertyContext)context; + property.startValue(); + + ValueContext value = new ValueContext(elementName, property); + contextStack.push(value); + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String) + */ + public void endElement(String uri, String localName, String qName) throws SAXException + { + try + { + // ensure context matches parse + ElementContext context = (ElementContext)contextStack.peek(); + QName elementName = QName.createQName(qName, importResolver); + if (!context.getElementName().equals(elementName)) + { + throw new InvalidSerializedDataException("Expected element " + context.getElementName() + " but was " + elementName); + } + + if (JCRSystemXMLExporter.NODE_QNAME.equals(elementName)) + { + processEndNode((NodeContext)context); + } + else if (JCRSystemXMLExporter.PROPERTY_QNAME.equals(elementName)) + { + processEndProperty((PropertyContext)context); + } + else if (JCRSystemXMLExporter.VALUE_QNAME.equals(elementName)) + { + processEndValue((ValueContext)context); + } + + // cleanup + contextStack.pop(); + } + catch(Exception e) + { + throw new SAXException("Failed to process element " + qName, e); + } + } + + /** + * Process end of Node element + * + * @param node + */ + private void processEndNode(NodeContext node) + { + // import node, if not already imported (this will be the case when no child nodes exist) + NodeRef nodeRef = node.getNodeRef(); + if (nodeRef == null) + { + nodeRef = node.getImporter().importNode(node); + node.setNodeRef(nodeRef); + } + + // signal end of node + node.getImporter().childrenImported(nodeRef); + } + + /** + * Process end of Property element + * + * @param context + * @throws InvalidSerializedDataException + * @throws RepositoryException + */ + private void processEndProperty(PropertyContext context) + throws InvalidSerializedDataException, RepositoryException + { + QName propertyName = context.getName(); + + // ensure a value has been provided + if (context.isNull()) + { + throw new InvalidSerializedDataException("Property " + propertyName + " must have a value"); + } + + // + // process known properties + // + + if (JCRPrimaryTypeProperty.PROPERTY_NAME.equals(propertyName)) + { + // apply type definition + if (!context.isNull()) + { + QName typeQName = QName.createQName(context.getValues().get(0).toString(), importResolver); + TypeDefinition typeDef = context.getDictionaryService().getType(typeQName); + if (typeDef == null) + { + throw new InvalidTypeException(typeQName); + } + + // update node context + context.getNode().setTypeDefinition(typeDef); + } + } + else if (JCRMixinTypesProperty.PROPERTY_NAME.equals(propertyName)) + { + // apply aspect definitions + List values = context.getValues(); + for (StringBuffer value : values) + { + QName aspectQName = QName.createQName(value.toString(), importResolver); + + // ignore JCR specific aspects + if (!(JCRNamespace.JCR_URI.equals(aspectQName.getNamespaceURI()) || + JCRNamespace.MIX_URI.equals(aspectQName.getNamespaceURI()))) + { + AspectDefinition aspectDef = context.getDictionaryService().getAspect(aspectQName); + if (aspectDef == null) + { + throw new InvalidTypeException(aspectQName); + } + context.getNode().addAspect(aspectDef); + } + } + } + else if (JCRUUIDProperty.PROPERTY_NAME.equals(propertyName)) + { + StringBuffer value = context.getValues().get(0); + context.getNode().setUUID(value.toString()); + } + + // + // Note: ignore all other JCR specific properties + // + + else if (JCRNamespace.JCR_URI.equals(propertyName.getNamespaceURI())) + { + } + + // + // process all other properties + // + + else + { + // apply the property values to the node + NodeContext node = context.getNode(); + if (node.getTypeDefinition() == null) + { + throw new InvalidSerializedDataException("Node jcr:primaryType property has not been specified."); + } + + // determine data type of value + QName dataType = context.getType(); + DataTypeDefinition dataTypeDef = context.getDictionaryService().getDataType(dataType); + if (dataTypeDef == null) + { + throw new InvalidTypeException(dataType); + } + node.addDatatype(propertyName, dataTypeDef); + + // determine if multi-value property + if (context.isMultiValue()) + { + node.addPropertyCollection(propertyName); + } + + // add each value to the node + List values = context.getValues(); + for (StringBuffer value : values) + { + // first, cast value to appropriate type (using JCR converters) + Serializable objVal = (Serializable)session.getTypeConverter().convert(dataTypeDef, value.toString()); + String strValue = DefaultTypeConverter.INSTANCE.convert(String.class, objVal); + node.addProperty(propertyName, strValue); + } + } + } + + /** + * Process end of value element + * + * @param context + */ + private void processEndValue(ValueContext context) + { + PropertyContext property = context.getProperty(); + property.endValue(); + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#characters(char[], int, int) + */ + public void characters(char[] ch, int start, int length) throws SAXException + { + ElementContext context = (ElementContext)contextStack.peek(); + if (context instanceof ValueContext) + { + PropertyContext property = ((ValueContext)context).getProperty(); + property.appendCharacters(ch, start, length); + } + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int) + */ + public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException + { + ElementContext context = (ElementContext)contextStack.peek(); + if (context instanceof ValueContext) + { + PropertyContext property = ((ValueContext)context).getProperty(); + property.appendCharacters(ch, start, length); + } + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, java.lang.String) + */ + public void processingInstruction(String target, String data) throws SAXException + { + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String) + */ + public void skippedEntity(String name) throws SAXException + { + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException) + */ + public void warning(SAXParseException exception) throws SAXException + { + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException) + */ + public void error(SAXParseException exception) throws SAXException + { + } + + /* + * (non-Javadoc) + * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException) + */ + public void fatalError(SAXParseException exception) throws SAXException + { + } + +} diff --git a/source/java/org/alfresco/jcr/importer/PropertyContext.java b/source/java/org/alfresco/jcr/importer/PropertyContext.java new file mode 100644 index 0000000000..4cbbae4427 --- /dev/null +++ b/source/java/org/alfresco/jcr/importer/PropertyContext.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.importer; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.importer.view.ElementContext; +import org.alfresco.repo.importer.view.NodeContext; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.view.ImporterException; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO9075; +import org.alfresco.util.TempFileProvider; + + +/** + * Maintains state about currently imported Property + * + * @author David Caruana + * + */ +public class PropertyContext extends ElementContext +{ + private NodeContext parentContext; + private QName propertyName; + private QName propertyType; + + private List values = new ArrayList(); + private Map contentWriters = new HashMap(); + + + /** + * Construct + * + * @param elementName + * @param parentContext + * @param propertyName + * @param propertyType + */ + public PropertyContext(QName elementName, NodeContext parentContext, QName propertyName, QName propertyType) + { + super(elementName, parentContext.getDictionaryService(), parentContext.getImporter()); + this.parentContext = parentContext; + this.propertyName = propertyName; + this.propertyType = propertyType; + } + + /** + * Get node containing property + * + * @return node + */ + public NodeContext getNode() + { + return parentContext; + } + + /** + * Get property name + * + * @return property name + */ + public QName getName() + { + return propertyName; + } + + /** + * Get property type + * + * @return property type + */ + public QName getType() + { + return propertyType; + } + + /** + * Is property multi-valued? + * + * @return true => multi-valued; false => single value + */ + public boolean isMultiValue() + { + return values.size() > 1; + } + + /** + * Is null property value + * + * @return true => value has not been provided + */ + public boolean isNull() + { + return values.size() == 0; + } + + /** + * Get property values + * + * @return values + */ + public List getValues() + { + return values; + } + + /** + * Start a new property value + */ + public void startValue() + { + StringBuffer buffer = new StringBuffer(128); + if (propertyType.equals(DataTypeDefinition.CONTENT)) + { + // create temporary file to hold content + File tempFile = TempFileProvider.createTempFile("import", ".tmp"); + try + { + FileWriter tempWriter = new FileWriter(tempFile); + contentWriters.put(propertyName, tempWriter); + ContentData contentData = new ContentData(tempFile.getAbsolutePath(), MimetypeMap.MIMETYPE_BINARY, 0, tempWriter.getEncoding()); + buffer.append(contentData.toString()); + } + catch(IOException e) + { + throw new ImporterException("Failed to create temporary content holder for property " + propertyName, e); + } + } + values.add(buffer); + } + + /** + * End a property value + */ + public void endValue() + { + if (propertyType.equals(DataTypeDefinition.CONTENT)) + { + // close content writer + FileWriter tempWriter = contentWriters.get(propertyName); + try + { + tempWriter.close(); + contentWriters.remove(propertyName); + } + catch(IOException e) + { + throw new ImporterException("Failed to create temporary content holder for property " + propertyName, e); + } + } + else + { + // decode value + StringBuffer buffer = values.get(values.size() -1); + values.set(values.size() -1, new StringBuffer(ISO9075.decode(buffer.toString()))); + } + } + + /** + * Append property value characters + * + * @param ch + * @param start + * @param length + */ + public void appendCharacters(char[] ch, int start, int length) + { + if (propertyType.equals(DataTypeDefinition.CONTENT)) + { + FileWriter tempWriter = contentWriters.get(propertyName); + try + { + tempWriter.write(ch, start, length); + } + catch(IOException e) + { + throw new ImporterException("Failed to write temporary content for property " + propertyName, e); + } + } + else + { + StringBuffer buffer = values.get(values.size() -1); + buffer.append(ch, start, length); + } + } + +} diff --git a/source/java/org/alfresco/jcr/importer/ValueContext.java b/source/java/org/alfresco/jcr/importer/ValueContext.java new file mode 100644 index 0000000000..0d79fade6e --- /dev/null +++ b/source/java/org/alfresco/jcr/importer/ValueContext.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.importer; + +import org.alfresco.repo.importer.view.ElementContext; +import org.alfresco.service.namespace.QName; + +/** + * Maintains state about currently imported value + * + * @author David Caruana + */ +public class ValueContext extends ElementContext +{ + private PropertyContext property; + + + /** + * Construct + * + * @param elementName + * @param property + */ + public ValueContext(QName elementName, PropertyContext property) + { + super(elementName, property.getDictionaryService(), property.getImporter()); + this.property = property; + } + + /** + * Get property + * + * @return property holding value + */ + public PropertyContext getProperty() + { + return property; + } + +} diff --git a/source/java/org/alfresco/jcr/item/ChildAssocNodeIteratorImpl.java b/source/java/org/alfresco/jcr/item/ChildAssocNodeIteratorImpl.java new file mode 100644 index 0000000000..970fe28a71 --- /dev/null +++ b/source/java/org/alfresco/jcr/item/ChildAssocNodeIteratorImpl.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.item; + +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; + +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.jcr.util.AbstractRangeIterator; +import org.alfresco.service.cmr.repository.ChildAssociationRef; + + +/** + * Alfresco implementation of a Node Iterator + * + * @author David Caruana + */ +public class ChildAssocNodeIteratorImpl extends AbstractRangeIterator + implements NodeIterator +{ + private SessionImpl sessionImpl; + private List childAssocs; + + + /** + * Construct + * + * @param context session context + * @param nodes node list + */ + public ChildAssocNodeIteratorImpl(SessionImpl sessionImpl, List childAssocs) + { + this.sessionImpl = sessionImpl; + this.childAssocs = childAssocs; + } + + /* (non-Javadoc) + * @see javax.jcr.NodeIterator#nextNode() + */ + public Node nextNode() + { + long position = skip(); + ChildAssociationRef childAssocRef = childAssocs.get((int)position); + NodeImpl nodeImpl = new NodeImpl(sessionImpl, childAssocRef.getChildRef()); + return nodeImpl.getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.RangeIterator#getSize() + */ + public long getSize() + { + return childAssocs.size(); + } + + /* (non-Javadoc) + * @see java.util.Iterator#next() + */ + public Object next() + { + return nextNode(); + } + +} diff --git a/source/java/org/alfresco/jcr/item/ItemImpl.java b/source/java/org/alfresco/jcr/item/ItemImpl.java new file mode 100644 index 0000000000..b3b854436b --- /dev/null +++ b/source/java/org/alfresco/jcr/item/ItemImpl.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.item; + +import javax.jcr.AccessDeniedException; +import javax.jcr.InvalidItemStateException; +import javax.jcr.Item; +import javax.jcr.ItemExistsException; +import javax.jcr.ReferentialIntegrityException; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.UnsupportedRepositoryOperationException; +import javax.jcr.lock.LockException; +import javax.jcr.nodetype.ConstraintViolationException; +import javax.jcr.nodetype.NoSuchNodeTypeException; +import javax.jcr.version.VersionException; + +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; + + +/** + * Alfresco Implementation of an Item + * + * @author David Caruana + */ +public abstract class ItemImpl implements Item +{ + protected SessionImpl session; + + + /** + * Construct + * + * @param session + */ + public ItemImpl(SessionImpl session) + { + this.session = session; + } + + /** + * Get the Session implementation + * + * @return session implementation + */ + public SessionImpl getSessionImpl() + { + return session; + } + + /** + * Get the Item Proxy + * + * @return the proxy + */ + public abstract Item getProxy(); + + /* (non-Javadoc) + * @see javax.jcr.Item#getSession() + */ + public Session getSession() throws RepositoryException + { + return session.getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.Item#isNew() + */ + public boolean isNew() + { + return false; + } + + /* (non-Javadoc) + * @see javax.jcr.Item#isModified() + */ + public boolean isModified() + { + return false; + } + + /* (non-Javadoc) + * @see javax.jcr.Item#save() + */ + public void save() throws AccessDeniedException, ItemExistsException, ConstraintViolationException, InvalidItemStateException, ReferentialIntegrityException, VersionException, LockException, NoSuchNodeTypeException, RepositoryException + { + AlfrescoTransactionSupport.flush(); + } + + /* (non-Javadoc) + * @see javax.jcr.Item#refresh(boolean) + */ + public void refresh(boolean keepChanges) throws InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + +} diff --git a/source/java/org/alfresco/jcr/item/ItemResolver.java b/source/java/org/alfresco/jcr/item/ItemResolver.java new file mode 100644 index 0000000000..d9f950d72a --- /dev/null +++ b/source/java/org/alfresco/jcr/item/ItemResolver.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.item; + +import java.util.List; + +import javax.jcr.PathNotFoundException; + +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.search.SearchService; + + +/** + * Responsible for finding JCR Items (Nodes, Properties) from Alfresco equivalents + * + * @author David Caruana + * + */ +public class ItemResolver +{ + + /** + * Create an Item from a JCR Path + * + * @param context session context + * @param from starting node for path + * @param path the path + * @return the Item (Node or Property) + * @throws PathNotFoundException + */ + public static ItemImpl findItem(SessionImpl context, NodeRef from, String path) + throws PathNotFoundException + { + ItemImpl item = null; + + NodeRef nodeRef = getNodeRef(context, from, path); + if (nodeRef != null) + { + item = new NodeImpl(context, nodeRef); + } + else + { + // TODO: create property + } + + if (item == null) + { + throw new PathNotFoundException("Path " + path + " not found."); + } + + return item; + } + + /** + * Create an Node from a JCR Path + * + * @param context session context + * @param from starting node for path + * @param path the path + * @return the Item (Node or Property) + * @throws PathNotFoundException + */ + public static NodeImpl findNode(SessionImpl context, NodeRef from, String path) + throws PathNotFoundException + { + NodeRef nodeRef = getNodeRef(context, from, path); + if (nodeRef == null) + { + throw new PathNotFoundException("A node does not exist at path " + path + " relative to node " + from); + } + return new NodeImpl(context, nodeRef); + } + + /** + * Determine if Item exists + * + * @param context session context + * @param from starting node for path + * @param path the path + * @return true => exists, false => no it doesn't + */ + public static boolean itemExists(SessionImpl context, NodeRef from, String path) + { + boolean exists = nodeExists(context, from, path); + if (!exists) + { + // TODO: Check for property + } + return exists; + } + + /** + * Determine if Node exists + * + * @param context session context + * @param from starting node for path + * @param path the path + * @return true => exists, false => no it doesn't + */ + public static boolean nodeExists(SessionImpl context, NodeRef from, String path) + { + NodeRef nodeRef = getNodeRef(context, from, path); + return nodeRef != null; + } + + /** + * Gets the Node Reference for the node at the specified path + * + * @param context session context + * @param from the starting node for the path + * @param path the path + * @return the node reference (or null if not found) + */ + public static NodeRef getNodeRef(SessionImpl context, NodeRef from, String path) + { + NodeRef nodeRef = null; + + // TODO: Support JCR Path + // TODO: Catch malformed path and return false (per Specification) + SearchService search = context.getRepositoryImpl().getServiceRegistry().getSearchService(); + List nodeRefs = search.selectNodes(from, path, null, context.getNamespaceResolver(), false); + if (nodeRefs != null && nodeRefs.size() > 0) + { + nodeRef = nodeRefs.get(0); + } + + return nodeRef; + } + +} diff --git a/source/java/org/alfresco/jcr/item/JCRPath.java b/source/java/org/alfresco/jcr/item/JCRPath.java new file mode 100644 index 0000000000..8112a3db9c --- /dev/null +++ b/source/java/org/alfresco/jcr/item/JCRPath.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.item; + +import java.util.StringTokenizer; + +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; + +/** + * JCR Path Helper + * + * @author David Caruana + */ +public class JCRPath +{ + private Path path; + + /** + * Constuct path from string representation of path + * + * @param strPath + */ + public JCRPath(NamespacePrefixResolver resolver, String strPath) + { + // TODO: replace this simple parse for full path syntax + boolean root = false; + int pos = 0; + path = new Path(); + StringTokenizer tokenizer = new StringTokenizer(strPath, "/", true); + while (tokenizer.hasMoreTokens()) + { + String token = tokenizer.nextToken(); + if (pos == 0 && token.equals("/")) + { + root = true; + } + else if (!token.equals("/")) + { + if (root) + { + path.append(new RootSimpleElement(resolver, token)); + root = false; + } + else + { + path.append(new SimpleElement(resolver, token)); + } + } + pos++; + } + } + + /** + * Get the Path + * + * @return the underling path + */ + public Path getPath() + { + return path; + } + + + @Override + public String toString() + { + return path.toString(); + } + + /** + * Simple Path Element used for building JCR Paths + * + * @author David Caruana + */ + public static class SimpleElement extends Path.Element + { + private static final long serialVersionUID = -6510331182652872996L; + private QName path; + + /** + * @param resolver namespace prefix resolver + * @param path path element name + */ + public SimpleElement(QName path) + { + this.path = path; + } + + /** + * @param path path element name + */ + public SimpleElement(NamespacePrefixResolver resolver, String path) + { + this.path = QName.createQName(path, resolver); + } + + /** + * Get the QName representation of Path + */ + public QName getQName() + { + return path; + } + + @Override + public String getElementString() + { + return path.toString(); + } + + @Override + public String getPrefixedString(NamespacePrefixResolver resolver) + { + return path.toPrefixString(resolver); + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object o) + { + if(o == this) + { + return true; + } + if(!(o instanceof SimpleElement)) + { + return false; + } + SimpleElement other = (SimpleElement)o; + return this.path.equals(other.path); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() + { + return path.hashCode(); + } + + } + + /** + * Root Path Element + * + * @author David Caruana + */ + public static class RootSimpleElement extends SimpleElement + { + private static final long serialVersionUID = -4827016063963328324L; + + /** + * Construct + * + * @param path + */ + public RootSimpleElement(NamespacePrefixResolver resolver, String path) + { + super(resolver, path); + } + + @Override + public String getElementString() + { + return "/" + super.getElementString(); + } + + @Override + public String getPrefixedString(NamespacePrefixResolver resolver) + { + return "/" + super.getPrefixedString(resolver); + } + } + +} diff --git a/source/java/org/alfresco/jcr/item/JCRPatternMatch.java b/source/java/org/alfresco/jcr/item/JCRPatternMatch.java new file mode 100644 index 0000000000..f37bb840ce --- /dev/null +++ b/source/java/org/alfresco/jcr/item/JCRPatternMatch.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.item; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.QNamePattern; + +/** + * Checks if the QName matches the passed JCR pattern. + * + * The pattern may be a full name or a partial name with one or more wildcard + * characters ("*"), or a disjunction (using the "|" character to represent + * logical OR) of these. For example, + * + * isMatch("jcr:* | myapp:report | my doc") + * + * Note that leading and trailing whitespace around a disjunct is ignored, + * but whitespace within a disjunct forms part of the pattern to be matched. + * + * The EBNF for namePattern is: + * + * namePattern ::= disjunct {'|' disjunct} + * disjunct ::= name [':' name] + * name ::= '*' | ['*'] fragment {'*' fragment} ['*'] + * fragment ::= char {char} + * char ::= nonspace | ' ' + * nonspace ::= (* Any Unicode character except: '/', ':', '[', ']', '*', ''', '"', '|' or any whitespace character *) + */ +public class JCRPatternMatch implements QNamePattern +{ + private List searches = new ArrayList(); + private NamespacePrefixResolver resolver; + + + /** + * Construct + * @param pattern JCR Pattern + * @param resolver Namespace Prefix Resolver + */ + public JCRPatternMatch(String pattern, NamespacePrefixResolver resolver) + { + // TODO: Check for valid pattern + + // Convert to regular expression + String regexPattern = pattern.replaceAll("\\*", ".*"); + + // Split into independent search strings + StringTokenizer tokenizer = new StringTokenizer(regexPattern, "|", false); + while (tokenizer.hasMoreTokens()) + { + String disjunct = tokenizer.nextToken().trim(); + this.searches.add(disjunct); + } + + this.resolver = resolver; + } + + /* (non-Javadoc) + * @see org.alfresco.service.namespace.QNamePattern#isMatch(org.alfresco.service.namespace.QName) + */ + public boolean isMatch(QName qname) + { + String prefixedName = qname.toPrefixString(resolver); + for (String search : searches) + { + if (prefixedName.matches(search)) + { + return true; + } + } + return false; + } + +} diff --git a/source/java/org/alfresco/jcr/item/JCRTypeConverter.java b/source/java/org/alfresco/jcr/item/JCRTypeConverter.java new file mode 100644 index 0000000000..1837e3cacf --- /dev/null +++ b/source/java/org/alfresco/jcr/item/JCRTypeConverter.java @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.item; + +import java.io.InputStream; +import java.util.Calendar; +import java.util.Date; + +import javax.jcr.RepositoryException; +import javax.jcr.ValueFormatException; + +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.repository.datatype.TypeConversionException; +import org.alfresco.service.cmr.repository.datatype.TypeConverter; +import org.alfresco.service.namespace.NamespaceException; +import org.alfresco.service.namespace.QName; + + +/** + * Responsible for converting Alfresco values to JCR values. + * + * @author David Caruana + * + */ +public class JCRTypeConverter +{ + private SessionTypeConverter jcrTypeConverter; + + /** + * Construct + * + * @param session + */ + public JCRTypeConverter(SessionImpl session) + { + this.jcrTypeConverter = new SessionTypeConverter(session); + } + + /** + * Get the underlying Converter + * + * @return type converter + */ + public TypeConverter getConverter() + { + return jcrTypeConverter; + } + + + /** + * Convert to JCR Reference Value + * + * @param session + * @param value + * @return + * @throws ValueFormatException + * @throws RepositoryException + */ + public NodeImpl referenceValue(Object value) throws ValueFormatException, RepositoryException + { + NodeRef nodeRef = (NodeRef)convert(NodeRef.class, value); + return new NodeImpl(jcrTypeConverter.getSession(), nodeRef); + } + + /** + * Convert to JCR String Value + * + * @param value + * @return + * @throws ValueFormatException + * @throws RepositoryException + */ + public String stringValue(Object value) throws ValueFormatException, RepositoryException + { + return (String)convert(String.class, value); + } + + /** + * Convert to JCR Stream Value + * + * @param value + * @return + * @throws IllegalStateException + * @throws RepositoryException + */ + public InputStream streamValue(Object value) throws IllegalStateException, RepositoryException + { + return (InputStream)convert(InputStream.class, value); + } + + /** + * Convert to JCR Long Value + * + * @param value + * @return + * @throws ValueFormatException + * @throws IllegalStateException + * @throws RepositoryException + */ + public long longValue(Object value) throws ValueFormatException, IllegalStateException, RepositoryException + { + try + { + return jcrTypeConverter.longValue(value); + } + catch(Exception e) + { + translateException(e); + throw new RepositoryException(e); + } + } + + /** + * Convert to JCR Double Value + * + * @param value + * @return + * @throws ValueFormatException + * @throws IllegalStateException + * @throws RepositoryException + */ + public double doubleValue(Object value) throws ValueFormatException, IllegalStateException, RepositoryException + { + try + { + return jcrTypeConverter.doubleValue(value); + } + catch(Exception e) + { + translateException(e); + throw new RepositoryException(e); + } + } + + /** + * Convert to JCR Date Value + * + * @param value + * @return + * @throws ValueFormatException + * @throws IllegalStateException + * @throws RepositoryException + */ + public Calendar dateValue(Object value) throws ValueFormatException, IllegalStateException, RepositoryException + { + Date date = (Date)convert(Date.class, value); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar; + } + + /** + * Convert to JCR Boolean Value + * + * @param value + * @return + * @throws ValueFormatException + * @throws IllegalStateException + * @throws RepositoryException + */ + public boolean booleanValue(Object value) throws ValueFormatException, IllegalStateException, RepositoryException + { + try + { + return jcrTypeConverter.booleanValue(value); + } + catch(Exception e) + { + translateException(e); + throw new RepositoryException(e); + } + } + + /** + * Convert to JCR Name Value + * + * @param value + * @return + * @throws ValueFormatException + * @throws IllegalStateException + * @throws RepositoryException + */ + public QName nameValue(Object value) throws ValueFormatException, IllegalStateException, RepositoryException + { + return convert(QName.class, value); + } + + /** + * Convert to JCR Path Value + * + * @param value + * @return + * @throws ValueFormatException + * @throws IllegalStateException + * @throws RepositoryException + */ + public Path pathValue(Object value) throws ValueFormatException, IllegalStateException, RepositoryException + { + return convert(Path.class, value); + } + + + /** + * General conversion method using JCR converters + * + * @param propertyType datatype to convert to + * @param value the value to convert + * @return the converted value + * @throws RepositoryException + */ + public final Object convert(DataTypeDefinition propertyType, Object value) + throws RepositoryException + { + try + { + return jcrTypeConverter.convert(propertyType, value); + } + catch(Exception e) + { + translateException(e); + throw new RepositoryException(e); + } + } + + /** + * General conversion method using JCR converters + * + * @param class + * @param c class + * @param value value to convert + * @return converted value + * @throws RepositoryException + */ + public final T convert(Class c, Object value) + throws RepositoryException + { + try + { + return jcrTypeConverter.convert(c, value); + } + catch(Exception e) + { + translateException(e); + throw new RepositoryException(e); + } + } + + /** + * Catch and translate value conversion errors + * + * @param e exception to translate + * @throws ValueFormatException value formatting exception + */ + private static void translateException(Exception e) throws ValueFormatException + { + if (e instanceof TypeConversionException || + e instanceof NumberFormatException) + { + throw new ValueFormatException(e); + } + } + + + /** + * Data Type Converter that takes into account JCR session context + * + * @author David Caruana + */ + private static class SessionTypeConverter extends TypeConverter + { + private SessionImpl session; + + /** + * Construct + * + * @param session session context + */ + public SessionTypeConverter(SessionImpl session) + { + this.session = session; + + + /** + * Converter to translating string to QName as prefix:localName + */ + addConverter(String.class, QName.class, new TypeConverter.Converter() + { + public QName convert(String source) + { + try + { + return QName.createQName(source, SessionTypeConverter.this.session.getNamespaceResolver()); + } + catch(NamespaceException e) + { + throw new TypeConversionException("Cannot convert " + source + " to qualified name", e); + } + } + }); + + /** + * Converter to translating string to QName as prefix:localName + */ + addConverter(String.class, Path.class, new TypeConverter.Converter() + { + public Path convert(String source) + { + try + { + return new JCRPath(SessionTypeConverter.this.session.getNamespaceResolver(), source).getPath(); + } + catch(NamespaceException e) + { + throw new TypeConversionException("Cannot convert " + source + " to qualified name", e); + } + } + }); + + /** + * Converter for translating QName to string as prefix:localName + */ + addConverter(QName.class, String.class, new TypeConverter.Converter() + { + public String convert(QName source) + { + try + { + return source.toPrefixString(SessionTypeConverter.this.session.getNamespaceResolver()); + } + catch(NamespaceException e) + { + throw new TypeConversionException("Cannot convert " + source + " to qualified name", e); + } + } + }); + + /** + * Converter for translating Path to string as prefix:localName + */ + addConverter(Path.class, String.class, new TypeConverter.Converter() + { + public String convert(Path source) + { + try + { + return source.toPrefixString(SessionTypeConverter.this.session.getNamespaceResolver()); + } + catch(NamespaceException e) + { + throw new TypeConversionException("Cannot convert " + source + " to qualified name", e); + } + } + }); + + /** + * Converter for translating Node Ref to JCR Id + */ + addConverter(NodeRef.class, String.class, new TypeConverter.Converter() + { + public String convert(NodeRef source) + { + return source.getId(); + } + }); + } + + /** + * Get the session + * + * @return session + */ + public SessionImpl getSession() + { + return session; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.repository.datatype.TypeConverter#getConverter(java.lang.Class, java.lang.Class) + */ + @Override + @SuppressWarnings("unchecked") + public Converter getConverter(Class source, Class dest) + { + Converter converter = super.getConverter(source, dest); + if (converter == null) + { + converter = DefaultTypeConverter.INSTANCE.getConverter(source, dest); + if (converter instanceof DynamicTwoStageConverter) + { + DynamicTwoStageConverter dynamic = (DynamicTwoStageConverter)converter; + converter = addDynamicTwoStageConverter(dynamic.getFrom(), dynamic.getIntermediate(), dynamic.getTo()); + } + } + return converter; + } + } + +} diff --git a/source/java/org/alfresco/jcr/item/LockImpl.java b/source/java/org/alfresco/jcr/item/LockImpl.java new file mode 100644 index 0000000000..0a598faf14 --- /dev/null +++ b/source/java/org/alfresco/jcr/item/LockImpl.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.item; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.lock.Lock; +import javax.jcr.lock.LockException; + +import org.alfresco.jcr.util.JCRProxyFactory; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.lock.LockService; +import org.alfresco.service.cmr.lock.LockStatus; +import org.alfresco.service.cmr.repository.NodeService; + +/** + * Alfresco implementation of a JCR Lock + * + * @author David Caruana + */ +public class LockImpl implements Lock +{ + + private NodeImpl node; + private Lock proxy = null; + + + /** + * Constructor + * + * @param node node holding lock + */ + public LockImpl(NodeImpl node) + { + this.node = node; + } + + /** + * Create proxied JCR Lock + * + * @return lock + */ + public Lock getProxy() + { + if (proxy == null) + { + proxy = (Lock)JCRProxyFactory.create(this, Lock.class, node.session); + } + return proxy; + } + + /* + * (non-Javadoc) + * @see javax.jcr.lock.Lock#getLockOwner() + */ + public String getLockOwner() + { + String lockOwner = null; + NodeService nodeService = node.session.getRepositoryImpl().getServiceRegistry().getNodeService(); + if (nodeService.hasAspect(node.getNodeRef(), ContentModel.ASPECT_LOCKABLE)) + { + lockOwner = (String)nodeService.getProperty(node.getNodeRef(), ContentModel.PROP_LOCK_OWNER); + } + return lockOwner; + } + + /* + * (non-Javadoc) + * @see javax.jcr.lock.Lock#isDeep() + */ + public boolean isDeep() + { + return false; + } + + /* + * (non-Javadoc) + * @see javax.jcr.lock.Lock#getNode() + */ + public Node getNode() + { + return node.getProxy(); + } + + /* + * (non-Javadoc) + * @see javax.jcr.lock.Lock#getLockToken() + */ + public String getLockToken() + { + LockService lockService = node.session.getRepositoryImpl().getServiceRegistry().getLockService(); + LockStatus lockStatus = lockService.getLockStatus(node.getNodeRef()); + return lockStatus.equals(LockStatus.LOCK_OWNER) ? node.getNodeRef().toString() : null; + } + + /* + * (non-Javadoc) + * @see javax.jcr.lock.Lock#isLive() + */ + public boolean isLive() throws RepositoryException + { + return getLockToken() == null ? false : true; + } + + /* + * (non-Javadoc) + * @see javax.jcr.lock.Lock#isSessionScoped() + */ + public boolean isSessionScoped() + { + return false; + } + + /* + * (non-Javadoc) + * @see javax.jcr.lock.Lock#refresh() + */ + public void refresh() throws LockException, RepositoryException + { + // note: for now, this is a noop + } + +} diff --git a/source/java/org/alfresco/jcr/item/NodeImpl.java b/source/java/org/alfresco/jcr/item/NodeImpl.java new file mode 100644 index 0000000000..a6ef2f0c7e --- /dev/null +++ b/source/java/org/alfresco/jcr/item/NodeImpl.java @@ -0,0 +1,1239 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.item; + +import java.io.InputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.jcr.AccessDeniedException; +import javax.jcr.InvalidItemStateException; +import javax.jcr.Item; +import javax.jcr.ItemExistsException; +import javax.jcr.ItemNotFoundException; +import javax.jcr.ItemVisitor; +import javax.jcr.MergeException; +import javax.jcr.NoSuchWorkspaceException; +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.PathNotFoundException; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.RepositoryException; +import javax.jcr.UnsupportedRepositoryOperationException; +import javax.jcr.Value; +import javax.jcr.ValueFormatException; +import javax.jcr.lock.Lock; +import javax.jcr.lock.LockException; +import javax.jcr.nodetype.ConstraintViolationException; +import javax.jcr.nodetype.NoSuchNodeTypeException; +import javax.jcr.nodetype.NodeDefinition; +import javax.jcr.nodetype.NodeType; +import javax.jcr.version.Version; +import javax.jcr.version.VersionException; +import javax.jcr.version.VersionHistory; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.jcr.api.JCRNodeRef; +import org.alfresco.jcr.dictionary.ClassMap; +import org.alfresco.jcr.dictionary.NodeDefinitionImpl; +import org.alfresco.jcr.dictionary.NodeTypeImpl; +import org.alfresco.jcr.item.property.PropertyResolver; +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.jcr.util.JCRProxyFactory; +import org.alfresco.jcr.version.VersionHistoryImpl; +import org.alfresco.jcr.version.VersionImpl; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.version.VersionModel; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.lock.LockService; +import org.alfresco.service.cmr.lock.LockStatus; +import org.alfresco.service.cmr.lock.LockType; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.Path.Element; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.version.VersionService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.util.ParameterCheck; + + +/** + * Alfresco Implementation of a JCR Node + * + * @author David Caruana + */ +public class NodeImpl extends ItemImpl implements Node +{ + /** Node Reference to wrap */ + private NodeRef nodeRef; + + /** Proxy */ + private Node proxy = null; + + + /** + * Construct + * + * @param context session context + * @param nodeRef node reference to wrap + */ + public NodeImpl(SessionImpl context, NodeRef nodeRef) + { + super(context); + this.nodeRef = nodeRef; + } + + /** + * Get Node Proxy + * + * @param nodeImpl + * @return + */ + @Override + public Node getProxy() + { + if (proxy == null) + { + proxy = (Node)JCRProxyFactory.create(this, Node.class, session); + } + return proxy; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#addNode(java.lang.String) + */ + public Node addNode(String relPath) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, RepositoryException + { + return addNode(relPath, null); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#addNode(java.lang.String, java.lang.String) + */ + public Node addNode(String relPath, String primaryNodeTypeName) throws ItemExistsException, PathNotFoundException, NoSuchNodeTypeException, LockException, VersionException, ConstraintViolationException, RepositoryException + { + ParameterCheck.mandatoryString("relPath", relPath); + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + DictionaryService dictionaryService = session.getRepositoryImpl().getServiceRegistry().getDictionaryService(); + + // Determine parent node reference and new node name + Path path = new JCRPath(session.getNamespaceResolver(), relPath).getPath(); + QName nodeName = null; + NodeRef parentRef = null; + if (path.size() == 1) + { + parentRef = nodeRef; + nodeName = ((JCRPath.SimpleElement)path.get(0)).getQName(); + } + else + { + Path parentPath = path.subPath(path.size() -2); + parentRef = ItemResolver.getNodeRef(session, nodeRef, parentPath.toPrefixString(session.getNamespaceResolver())); + if (parentRef == null) + { + throw new PathNotFoundException("Path '" + relPath + "' does not exist from node " + nodeRef); + } + nodeName = ((JCRPath.SimpleElement)path.get(path.size() -1)).getQName(); + } + + // Check for invalid node name + // TODO: Replace with proper name validation + if (nodeName.getLocalName().indexOf('[') != -1 || nodeName.getLocalName().indexOf(']') != -1) + { + throw new RepositoryException("Node name '" + nodeName + "' is invalid"); + } + + // Determine child association to add node under + ChildAssociationDefinition childAssocDef = null; + QName nodeType = null; + if (primaryNodeTypeName == null || primaryNodeTypeName.length() == 0) + { + childAssocDef = getDefaultChildAssocDefForParent(nodeService, dictionaryService, parentRef); + nodeType = childAssocDef.getTargetClass().getName(); + } + else + { + nodeType = QName.createQName(primaryNodeTypeName, session.getNamespaceResolver()); + childAssocDef = getNodeTypeChildAssocDefForParent(nodeService, dictionaryService, parentRef, nodeType); + } + + // Create node + // Note: Integrity exception will be thrown when the node is saved + ChildAssociationRef childRef = nodeService.createNode(parentRef, childAssocDef.getName(), nodeName, nodeType); + NodeImpl nodeImpl = new NodeImpl(session, childRef.getChildRef()); + return nodeImpl.getProxy(); + } + + /** + * Get the default child association definition for the specified node + * + * @param nodeService node service + * @param dictionaryService dictionary service + * @param nodeRef node reference + * @return child association definition + */ + private ChildAssociationDefinition getDefaultChildAssocDefForParent(NodeService nodeService, DictionaryService dictionaryService, NodeRef nodeRef) + { + QName type = nodeService.getType(nodeRef); + Set aspects = nodeService.getAspects(nodeRef); + ClassDefinition classDef = dictionaryService.getAnonymousType(type, aspects); + Map childAssocs = classDef.getChildAssociations(); + if (childAssocs.size() != 1) + { + throw new AlfrescoRuntimeException("Cannot determine node type for child within parent " + nodeRef); + } + ChildAssociationDefinition childAssocDef = childAssocs.values().iterator().next(); + return childAssocDef; + } + + /** + * Get the child association definition whose target matches the specified node type for the specified node + * + * @param nodeService node service + * @param dictionaryService dictionary service + * @param nodeRef node reference + * @param nodeType node type to find child association definition for + * @return child association definition + */ + private ChildAssociationDefinition getNodeTypeChildAssocDefForParent(NodeService nodeService, DictionaryService dictionaryService, NodeRef nodeRef, QName nodeType) + { + ChildAssociationDefinition nodeTypeChildAssocDef = null; + QName type = nodeService.getType(nodeRef); + Set aspects = nodeService.getAspects(nodeRef); + ClassDefinition classDef = dictionaryService.getAnonymousType(type, aspects); + Map childAssocs = classDef.getChildAssociations(); + for (ChildAssociationDefinition childAssocDef : childAssocs.values()) + { + if (dictionaryService.isSubClass(nodeType, childAssocDef.getTargetClass().getName())) + { + if (nodeTypeChildAssocDef != null) + { + throw new AlfrescoRuntimeException("Cannot determine child association for node type '" + nodeType + " within parent " + nodeRef); + } + nodeTypeChildAssocDef = childAssocDef; + } + } + if (nodeTypeChildAssocDef == null) + { + throw new AlfrescoRuntimeException("Cannot determine child association for node type '" + nodeType + " within parent " + nodeRef); + } + return nodeTypeChildAssocDef; + } + + /* (non-Javadoc) + * @see javax.jcr.Item#remove() + */ + public void remove() throws VersionException, LockException, ConstraintViolationException, RepositoryException + { + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + + // Note: remove the primary child association, therefore forcing a delete of the node (including any secondary child + // associations) + ChildAssociationRef assocRef = nodeService.getPrimaryParent(nodeRef); + NodeRef parentRef = assocRef.getParentRef(); + if (parentRef == null) + { + throw new ConstraintViolationException("Cannot remove the root node"); + } + nodeService.removeChild(parentRef, nodeRef); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#orderBefore(java.lang.String, java.lang.String) + */ + public void orderBefore(String srcChildRelPath, String destChildRelPath) throws UnsupportedRepositoryOperationException, VersionException, ConstraintViolationException, ItemNotFoundException, LockException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#setProperty(java.lang.String, javax.jcr.Value) + */ + public Property setProperty(String name, Value value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + QName propertyName = QName.createQName(name, session.getNamespaceResolver()); + PropertyImpl property = new PropertyImpl(this, propertyName); + property.setPropertyValue(value, -1); + return (value == null ) ? null : property; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#setProperty(java.lang.String, javax.jcr.Value, int) + */ + public Property setProperty(String name, Value value, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + QName propertyName = QName.createQName(name, session.getNamespaceResolver()); + PropertyImpl property = new PropertyImpl(this, propertyName); + property.setPropertyValue(value, type); + return (value == null ) ? null : property; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#setProperty(java.lang.String, javax.jcr.Value[]) + */ + public Property setProperty(String name, Value[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + QName propertyName = QName.createQName(name, session.getNamespaceResolver()); + PropertyImpl property = new PropertyImpl(this, propertyName); + property.setPropertyValue(values, -1); + return (values == null ) ? null : property; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#setProperty(java.lang.String, javax.jcr.Value[], int) + */ + public Property setProperty(String name, Value[] values, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + QName propertyName = QName.createQName(name, session.getNamespaceResolver()); + PropertyImpl property = new PropertyImpl(this, propertyName); + property.setPropertyValue(values, type); + return (values == null ) ? null : property; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#setProperty(java.lang.String, java.lang.String[]) + */ + public Property setProperty(String name, String[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + QName propertyName = QName.createQName(name, session.getNamespaceResolver()); + PropertyImpl property = new PropertyImpl(this, propertyName); + property.setPropertyValue(values, -1); + return (values == null ) ? null : property; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#setProperty(java.lang.String, java.lang.String[], int) + */ + public Property setProperty(String name, String[] values, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + QName propertyName = QName.createQName(name, session.getNamespaceResolver()); + PropertyImpl property = new PropertyImpl(this, propertyName); + property.setPropertyValue(values, type); + return (values == null ) ? null : property; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#setProperty(java.lang.String, java.lang.String) + */ + public Property setProperty(String name, String value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + QName propertyName = QName.createQName(name, session.getNamespaceResolver()); + PropertyImpl property = new PropertyImpl(this, propertyName); + property.setPropertyValue(value, -1); + return (value == null ) ? null : property; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#setProperty(java.lang.String, java.lang.String, int) + */ + public Property setProperty(String name, String value, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + QName propertyName = QName.createQName(name, session.getNamespaceResolver()); + PropertyImpl property = new PropertyImpl(this, propertyName); + property.setPropertyValue(value, type); + return (value == null ) ? null : property; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#setProperty(java.lang.String, java.io.InputStream) + */ + public Property setProperty(String name, InputStream value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + QName propertyName = QName.createQName(name, session.getNamespaceResolver()); + PropertyImpl property = new PropertyImpl(this, propertyName); + property.setPropertyValue(value, -1); + return (value == null ) ? null : property; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#setProperty(java.lang.String, boolean) + */ + public Property setProperty(String name, boolean value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + QName propertyName = QName.createQName(name, session.getNamespaceResolver()); + PropertyImpl property = new PropertyImpl(this, propertyName); + property.setPropertyValue(value, -1); + return property; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#setProperty(java.lang.String, double) + */ + public Property setProperty(String name, double value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + QName propertyName = QName.createQName(name, session.getNamespaceResolver()); + PropertyImpl property = new PropertyImpl(this, propertyName); + property.setPropertyValue(value, -1); + return property; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#setProperty(java.lang.String, long) + */ + public Property setProperty(String name, long value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + QName propertyName = QName.createQName(name, session.getNamespaceResolver()); + PropertyImpl property = new PropertyImpl(this, propertyName); + property.setPropertyValue(value, -1); + return property; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#setProperty(java.lang.String, java.util.Calendar) + */ + public Property setProperty(String name, Calendar value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + QName propertyName = QName.createQName(name, session.getNamespaceResolver()); + PropertyImpl property = new PropertyImpl(this, propertyName); + property.setPropertyValue((value == null) ? null : value.getTime(), -1); + return (value == null ) ? null : property; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#setProperty(java.lang.String, javax.jcr.Node) + */ + public Property setProperty(String name, Node value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + QName propertyName = QName.createQName(name, session.getNamespaceResolver()); + PropertyImpl property = new PropertyImpl(this, propertyName); + property.setPropertyValue((value == null) ? null : JCRNodeRef.getNodeRef(value), -1); + return (value == null ) ? null : property; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#getNode(java.lang.String) + */ + public Node getNode(String relPath) throws PathNotFoundException, RepositoryException + { + NodeImpl nodeImpl = ItemResolver.findNode(session, nodeRef, relPath); + return nodeImpl.getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#getNodes() + */ + public NodeIterator getNodes() throws RepositoryException + { + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + List childAssocs = nodeService.getChildAssocs(nodeRef); + NodeIterator iterator = new ChildAssocNodeIteratorImpl(session, childAssocs); + return iterator; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#getNodes(java.lang.String) + */ + public NodeIterator getNodes(String namePattern) throws RepositoryException + { + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + JCRPatternMatch match = new JCRPatternMatch(namePattern, session.getNamespaceResolver()); + List childAssocs = nodeService.getChildAssocs(nodeRef, RegexQNamePattern.MATCH_ALL, match); + NodeIterator iterator = new ChildAssocNodeIteratorImpl(session, childAssocs); + return iterator; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#getProperty(java.lang.String) + */ + public Property getProperty(String relPath) throws PathNotFoundException, RepositoryException + { + JCRPath jcrPath = new JCRPath(session.getNamespaceResolver(), relPath); + Path path = jcrPath.getPath(); + if (path.size() == 1) + { + QName propertyName = ((JCRPath.SimpleElement)path.get(0)).getQName(); + return PropertyResolver.createProperty(this, propertyName).getProxy(); + } + + ItemImpl itemImpl = ItemResolver.findItem(session, nodeRef, relPath); + if (itemImpl == null || !(itemImpl instanceof PropertyImpl)) + { + throw new PathNotFoundException("Property path " + relPath + " not found from node " + nodeRef); + } + return ((PropertyImpl)itemImpl).getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#getProperties() + */ + public PropertyIterator getProperties() throws RepositoryException + { + List properties = PropertyResolver.createProperties(this, null); + PropertyIterator iterator = new PropertyListIterator(properties); + return iterator; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#getProperties(java.lang.String) + */ + public PropertyIterator getProperties(String namePattern) throws RepositoryException + { + JCRPatternMatch match = new JCRPatternMatch(namePattern, session.getNamespaceResolver()); + List properties = PropertyResolver.createProperties(this, match); + PropertyIterator iterator = new PropertyListIterator(properties); + return iterator; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#getPrimaryItem() + */ + public Item getPrimaryItem() throws ItemNotFoundException, RepositoryException + { + // Note: Alfresco does not support the notion of primary item + throw new ItemNotFoundException(); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#getUUID() + */ + public String getUUID() throws UnsupportedRepositoryOperationException, RepositoryException + { + return nodeRef.getId(); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#getIndex() + */ + public int getIndex() throws RepositoryException + { + int index = 1; + String name = getName(); + if (name != null) + { + // TODO: Look at more efficient approach + SearchService searchService = session.getRepositoryImpl().getServiceRegistry().getSearchService(); + List siblings = searchService.selectNodes(nodeRef, "../" + name, null, session.getNamespaceResolver(), false); + for (NodeRef sibling : siblings) + { + if (sibling.equals(nodeRef)) + { + break; + } + index++; + } + } + return index; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#getReferences() + */ + public PropertyIterator getReferences() throws RepositoryException + { + // Note: Lookup for references not supported for now + return new PropertyListIterator(new ArrayList()); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#hasNode(java.lang.String) + */ + public boolean hasNode(String relPath) throws RepositoryException + { + return ItemResolver.nodeExists(session, nodeRef, relPath); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#hasProperty(java.lang.String) + */ + public boolean hasProperty(String relPath) throws RepositoryException + { + JCRPath jcrPath = new JCRPath(session.getNamespaceResolver(), relPath); + Path path = jcrPath.getPath(); + if (path.size() == 1) + { + QName propertyName = ((JCRPath.SimpleElement)path.get(0)).getQName(); + return PropertyResolver.hasProperty(this, propertyName); + } + + return ItemResolver.itemExists(session, nodeRef, relPath); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#hasNodes() + */ + public boolean hasNodes() throws RepositoryException + { + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + List childAssocs = nodeService.getChildAssocs(nodeRef); + return childAssocs.size() > 0; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#hasProperties() + */ + public boolean hasProperties() throws RepositoryException + { + // Note: nt:base has a mandatory primaryType property for which we don't have security access control + return true; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#getPrimaryNodeType() + */ + public NodeType getPrimaryNodeType() throws RepositoryException + { + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + QName type = nodeService.getType(nodeRef); + return session.getTypeManager().getNodeTypeImpl(type); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#getMixinNodeTypes() + */ + public NodeType[] getMixinNodeTypes() throws RepositoryException + { + // Add aspects defined by node + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + Set aspects = nodeService.getAspects(nodeRef); + NodeType[] nodeTypes = new NodeType[aspects.size() + 1]; + int i = 0; + for (QName aspect : aspects) + { + nodeTypes[i++] = session.getTypeManager().getNodeTypeImpl(aspect); + QName mixin = ClassMap.convertClassToType(aspect); + if (mixin != null) + { + nodeTypes[i++] = session.getTypeManager().getNodeTypeImpl(mixin); + } + } + + return nodeTypes; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#isNodeType(java.lang.String) + */ + public boolean isNodeType(String nodeTypeName) throws RepositoryException + { + QName nodeType = QName.createQName(nodeTypeName, session.getNamespaceResolver()); + + // is it one of standard types + if (nodeType.equals(NodeTypeImpl.MIX_REFERENCEABLE) || nodeType.equals(NodeTypeImpl.NT_BASE)) + { + return true; + } + + // map JCR mixins to Alfresco mixins + QName nodeClass = ClassMap.convertTypeToClass(nodeType); + if (nodeClass == null) + { + nodeClass = nodeType; + } + + // determine via class hierarchy + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + DictionaryService dictionaryService = session.getRepositoryImpl().getServiceRegistry().getDictionaryService(); + + // first, check the type + QName type = nodeService.getType(nodeRef); + if (dictionaryService.isSubClass(nodeClass, type)) + { + return true; + } + + // second, check the aspects + Set aspects = nodeService.getAspects(nodeRef); + for (QName aspect : aspects) + { + if (dictionaryService.isSubClass(nodeClass, aspect)) + { + return true; + } + } + + // no, its definitely not of the specified type + return false; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#addMixin(java.lang.String) + */ + public void addMixin(String mixinName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException + { + // map JCR mixins to Alfresco mixins + QName mixin = QName.createQName(mixinName, session.getNamespaceResolver()); + QName aspect = ClassMap.convertTypeToClass(mixin); + if (aspect == null) + { + aspect = mixin; + } + + // retrieve aspect definition + DictionaryService dictionaryService = session.getRepositoryImpl().getServiceRegistry().getDictionaryService(); + AspectDefinition aspectDef = dictionaryService.getAspect(aspect); + if (aspectDef == null) + { + throw new NoSuchNodeTypeException("Unknown mixin name '" + mixinName + "'"); + } + + // apply aspect + ClassMap.AddMixin addMixin = ClassMap.getAddMixin(aspect); + Map initialProperties = addMixin.preAddMixin(session, nodeRef); + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + nodeService.addAspect(nodeRef, aspect, initialProperties); + addMixin.postAddMixin(session, nodeRef); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#removeMixin(java.lang.String) + */ + public void removeMixin(String mixinName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException + { + // map JCR mixins to Alfresco mixins + QName mixin = QName.createQName(mixinName, session.getNamespaceResolver()); + QName aspect = ClassMap.convertTypeToClass(mixin); + if (aspect == null) + { + aspect = mixin; + } + + // retrieve aspect definition + DictionaryService dictionaryService = session.getRepositoryImpl().getServiceRegistry().getDictionaryService(); + AspectDefinition aspectDef = dictionaryService.getAspect(aspect); + if (aspectDef == null) + { + throw new NoSuchNodeTypeException("Unknown mixin name '" + mixinName + "'"); + } + + // check the node actually has the mixin + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + Set nodeAspects = nodeService.getAspects(nodeRef); + if (!nodeAspects.contains(aspect)) + { + throw new NoSuchNodeTypeException("Node " + nodeRef.getId() + " does not have the mixin " + mixin); + } + + // remove aspect + ClassMap.RemoveMixin removeMixin = ClassMap.getRemoveMixin(aspect); + removeMixin.preRemoveMixin(session, nodeRef); + nodeService.removeAspect(nodeRef, aspect); + removeMixin.postRemoveMixin(session, nodeRef); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#canAddMixin(java.lang.String) + */ + public boolean canAddMixin(String mixinName) throws NoSuchNodeTypeException, RepositoryException + { + // map JCR mixins to Alfresco mixins + QName mixin = QName.createQName(mixinName, session.getNamespaceResolver()); + QName aspect = ClassMap.convertTypeToClass(mixin); + if (aspect == null) + { + aspect = mixin; + } + + // retrieve aspect definition + DictionaryService dictionaryService = session.getRepositoryImpl().getServiceRegistry().getDictionaryService(); + AspectDefinition aspectDef = dictionaryService.getAspect(aspect); + if (aspectDef == null) + { + throw new NoSuchNodeTypeException("Unknown mixin name '" + mixinName + "'"); + } + + // TODO: check for write permission + + // check for locked node + LockService lockService = session.getRepositoryImpl().getServiceRegistry().getLockService(); + LockStatus lockStatus = lockService.getLockStatus(nodeRef); + if (lockStatus == LockStatus.LOCKED) + { + return false; + } + + // mixin addition is allowed + return true; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#getDefinition() + */ + public NodeDefinition getDefinition() throws RepositoryException + { + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + DictionaryService dictionaryService = session.getRepositoryImpl().getServiceRegistry().getDictionaryService(); + ChildAssociationRef childAssocRef = nodeService.getPrimaryParent(this.nodeRef); + ChildAssociationDefinition childAssocDef = (ChildAssociationDefinition)dictionaryService.getAssociation(childAssocRef.getTypeQName()); + NodeDefinition nodeDef = new NodeDefinitionImpl(session.getTypeManager(), childAssocDef); + return nodeDef; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#checkin() + */ + public Version checkin() throws VersionException, UnsupportedRepositoryOperationException, InvalidItemStateException, LockException, RepositoryException + { + // check this node is versionable + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + if (!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE)) + { + throw new UnsupportedRepositoryOperationException("Node " + nodeRef + " is not versionable"); + } + + Version version = null; + if (!isCheckedOut()) + { + // return current version + version = getBaseVersion(); + } + else + { + // create a new version snapshot + VersionService versionService = session.getRepositoryImpl().getServiceRegistry().getVersionService(); + org.alfresco.service.cmr.version.Version versionNode = versionService.createVersion(nodeRef, null); + org.alfresco.service.cmr.version.VersionHistory versionHistory = versionService.getVersionHistory(nodeRef); + version = new VersionImpl(new VersionHistoryImpl(session, versionHistory), versionNode).getProxy(); + + // set to 'read only' + LockService lockService = session.getRepositoryImpl().getServiceRegistry().getLockService(); + lockService.lock(nodeRef, LockType.READ_ONLY_LOCK); + } + + return version; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#checkout() + */ + public void checkout() throws UnsupportedRepositoryOperationException, LockException, RepositoryException + { + // check this node is versionable + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + if (!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE)) + { + throw new UnsupportedRepositoryOperationException("Node " + nodeRef + " is not versionable"); + } + + // remove 'read only' lock + if (!isCheckedOut()) + { + LockService lockService = session.getRepositoryImpl().getServiceRegistry().getLockService(); + lockService.unlock(nodeRef); + } + } + + /* (non-Javadoc) + * @see javax.jcr.Node#doneMerge(javax.jcr.version.Version) + */ + public void doneMerge(Version version) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#cancelMerge(javax.jcr.version.Version) + */ + public void cancelMerge(Version version) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#update(java.lang.String) + */ + public void update(String srcWorkspaceName) throws NoSuchWorkspaceException, AccessDeniedException, LockException, InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#merge(java.lang.String, boolean) + */ + public NodeIterator merge(String srcWorkspace, boolean bestEffort) throws NoSuchWorkspaceException, AccessDeniedException, MergeException, LockException, InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#getCorrespondingNodePath(java.lang.String) + */ + public String getCorrespondingNodePath(String workspaceName) throws ItemNotFoundException, NoSuchWorkspaceException, AccessDeniedException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#isCheckedOut() + */ + public boolean isCheckedOut() throws RepositoryException + { + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + if (!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE)) + { + // it's not versionable, therefore it's checked-out and writable + // TODO: Do not yet take into consideration versionable ancestor + return true; + } + + // it's versionable, use the lock to determine if it's checked-out + LockService lockService = session.getRepositoryImpl().getServiceRegistry().getLockService(); + LockType lockType = lockService.getLockType(nodeRef); + if (lockType == null) + { + // it's not locked at all + return true; + } + + // it's only checked-in when a read-only locked + return (lockType.equals(LockType.READ_ONLY_LOCK)) ? false : true; + } + + /* (non-Javadoc) + * @see javax.jcr.Node#restore(java.lang.String, boolean) + */ + public void restore(String versionName, boolean removeExisting) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException + { + // check this node is versionable + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + if (!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE)) + { + throw new UnsupportedRepositoryOperationException("Node " + nodeRef + " is not versionable"); + } + + // retrieve version for label + VersionService versionService = session.getRepositoryImpl().getServiceRegistry().getVersionService(); + org.alfresco.service.cmr.version.VersionHistory versionHistory = versionService.getVersionHistory(nodeRef); + org.alfresco.service.cmr.version.Version version = versionHistory.getVersion(versionName); + if (version == null) + { + throw new VersionException("Version name " + versionName + " does not exist in the version history of node " + nodeRef); + } + + // unlock if necessary + LockService lockService = session.getRepositoryImpl().getServiceRegistry().getLockService(); + LockType lockType = lockService.getLockType(nodeRef); + if (lockType != null) + { + lockService.unlock(nodeRef); + } + + // revert to version + versionService.revert(nodeRef, version); + lockService.lock(nodeRef, LockType.READ_ONLY_LOCK); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#restore(javax.jcr.version.Version, boolean) + */ + public void restore(Version version, boolean removeExisting) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, RepositoryException + { + restore(version.getName(), removeExisting); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#restore(javax.jcr.version.Version, java.lang.String, boolean) + */ + public void restore(Version version, String relPath, boolean removeExisting) throws PathNotFoundException, ItemExistsException, VersionException, ConstraintViolationException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#restoreByLabel(java.lang.String, boolean) + */ + public void restoreByLabel(String versionLabel, boolean removeExisting) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#getVersionHistory() + */ + public VersionHistory getVersionHistory() throws UnsupportedRepositoryOperationException, RepositoryException + { + // check this node is versionable + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + if (!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE)) + { + throw new UnsupportedRepositoryOperationException("Node " + nodeRef + " is not versionable"); + } + + // construct version history + VersionService versionService = session.getRepositoryImpl().getServiceRegistry().getVersionService(); + org.alfresco.service.cmr.version.VersionHistory versionHistory = versionService.getVersionHistory(nodeRef); + return new VersionHistoryImpl(session, versionHistory).getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#getBaseVersion() + */ + public Version getBaseVersion() throws UnsupportedRepositoryOperationException, RepositoryException + { + // check this node is versionable + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + if (!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE)) + { + throw new UnsupportedRepositoryOperationException("Node " + nodeRef + " is not versionable"); + } + + // construct version + VersionService versionService = session.getRepositoryImpl().getServiceRegistry().getVersionService(); + org.alfresco.service.cmr.version.VersionHistory versionHistory = versionService.getVersionHistory(nodeRef); + org.alfresco.service.cmr.version.Version version = versionService.getCurrentVersion(nodeRef); + return new VersionImpl(new VersionHistoryImpl(session, versionHistory), version).getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#lock(boolean, boolean) + */ + public Lock lock(boolean isDeep, boolean isSessionScoped) throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException + { + // note: alfresco does not yet support session scoped locks + if (isSessionScoped) + { + throw new UnsupportedRepositoryOperationException("Session scope locking is not supported."); + } + + // check this node is lockable + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + if (!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)) + { + throw new LockException("Node " + nodeRef + " does is not lockable."); + } + + // lock the node + LockService lockService = session.getRepositoryImpl().getServiceRegistry().getLockService(); + lockService.lock(nodeRef, LockType.WRITE_LOCK, 0, isDeep); + + // return lock + return new LockImpl(this).getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#getLock() + */ + public Lock getLock() throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, RepositoryException + { + // check this node is lockable + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + if (!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)) + { + throw new LockException("Node " + nodeRef + " does is not lockable."); + } + + // return lock + return new LockImpl(this).getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#unlock() + */ + public void unlock() throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException + { + // check this node is lockable + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + if (!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)) + { + throw new LockException("Node " + nodeRef + " does is not lockable."); + } + + // unlock + LockService lockService = session.getRepositoryImpl().getServiceRegistry().getLockService(); + lockService.unlock(nodeRef, true); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#holdsLock() + */ + public boolean holdsLock() throws RepositoryException + { + // note: for now, alfresco doesn't distinguish between lock holder and locked + return isLocked(); + } + + /* (non-Javadoc) + * @see javax.jcr.Node#isLocked() + */ + public boolean isLocked() throws RepositoryException + { + LockService lockService = session.getRepositoryImpl().getServiceRegistry().getLockService(); + LockStatus lockStatus = lockService.getLockStatus(getNodeRef()); + return lockStatus.equals(LockStatus.LOCK_OWNER) || lockStatus.equals(LockStatus.LOCKED); + } + + /* (non-Javadoc) + * @see javax.jcr.Item#getName() + */ + public String getName() throws RepositoryException + { + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + ChildAssociationRef parentAssoc = nodeService.getPrimaryParent(nodeRef); + QName childName = parentAssoc.getQName(); + return (childName == null) ? "" : childName.toPrefixString(session.getNamespaceResolver()); + } + + /* (non-Javadoc) + * @see javax.jcr.Item#isNode() + */ + public boolean isNode() + { + return true; + } + + /* (non-Javadoc) + * @see javax.jcr.Item#getParent() + */ + public Node getParent() throws ItemNotFoundException, AccessDeniedException, RepositoryException + { + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + ChildAssociationRef parentAssoc = nodeService.getPrimaryParent(nodeRef); + if (parentAssoc == null || parentAssoc.getParentRef() == null) + { + // TODO: Distinguish between ItemNotFound and AccessDenied + throw new ItemNotFoundException("Parent of node " + nodeRef + " does not exist."); + } + NodeImpl nodeImpl = new NodeImpl(session, parentAssoc.getParentRef()); + return nodeImpl.getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.Item#getPath() + */ + public String getPath() throws RepositoryException + { + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + SearchService searchService = session.getRepositoryImpl().getServiceRegistry().getSearchService(); + Path path = nodeService.getPath(nodeRef); + + // Add indexes for same name siblings + // TODO: Look at more efficient approach + for (int i = path.size() - 1; i >= 0; i--) + { + Path.Element pathElement = path.get(i); + if (i > 0 && pathElement instanceof Path.ChildAssocElement) + { + int index = 1; + String searchPath = path.subPath(i).toPrefixString(session.getNamespaceResolver()); + List siblings = searchService.selectNodes(nodeRef, searchPath, null, session.getNamespaceResolver(), false); + if (siblings.size() > 1) + { + ChildAssociationRef childAssoc = ((Path.ChildAssocElement)pathElement).getRef(); + NodeRef childRef = childAssoc.getChildRef(); + for (NodeRef sibling : siblings) + { + if (sibling.equals(childRef)) + { + childAssoc.setNthSibling(index); + break; + } + index++; + } + } + } + } + + return path.toPrefixString(session.getNamespaceResolver()); + } + + /* (non-Javadoc) + * @see javax.jcr.Item#getDepth() + */ + public int getDepth() throws RepositoryException + { + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + Path path = nodeService.getPath(nodeRef); + // Note: Root is at depth 0 + return path.size() -1; + } + + /* (non-Javadoc) + * @see javax.jcr.Item#getAncestor(int) + */ + public Item getAncestor(int depth) throws ItemNotFoundException, AccessDeniedException, RepositoryException + { + // Retrieve primary parent path for node + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + Path path = nodeService.getPath(nodeRef); + if (depth < 0 || depth > (path.size() - 1)) + { + throw new ItemNotFoundException("Ancestor at depth " + depth + " not found for node " + nodeRef); + } + + // Extract path element at requested depth + Element element = path.get(depth); + if (!(element instanceof Path.ChildAssocElement)) + { + throw new RepositoryException("Path element at depth " + depth + " is not a node"); + } + Path.ChildAssocElement childAssocElement = (Path.ChildAssocElement)element; + + // Create node + NodeRef ancestorNodeRef = childAssocElement.getRef().getChildRef(); + NodeImpl nodeImpl = new NodeImpl(session, ancestorNodeRef); + return nodeImpl.getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.Item#isSame(javax.jcr.Item) + */ + public boolean isSame(Item otherItem) throws RepositoryException + { + return getProxy().equals(otherItem); + } + + /* (non-Javadoc) + * @see javax.jcr.Item#accept(javax.jcr.ItemVisitor) + */ + public void accept(ItemVisitor visitor) throws RepositoryException + { + visitor.visit(getProxy()); + } + + /** + * Gets the Alfresco Node Reference + * + * @return the node reference + */ + public NodeRef getNodeRef() + { + return nodeRef; + } + + @Override + public boolean equals(Object obj) + { + if (obj == this) + { + return true; + } + if (!(obj instanceof NodeImpl)) + { + return false; + } + NodeImpl other = (NodeImpl)obj; + return this.nodeRef.equals(other.nodeRef); + } + + @Override + public int hashCode() + { + return nodeRef.hashCode(); + } + +} diff --git a/source/java/org/alfresco/jcr/item/NodeRefNodeIteratorImpl.java b/source/java/org/alfresco/jcr/item/NodeRefNodeIteratorImpl.java new file mode 100644 index 0000000000..9873818095 --- /dev/null +++ b/source/java/org/alfresco/jcr/item/NodeRefNodeIteratorImpl.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.item; + +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.query.QueryResult; + +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.jcr.util.AbstractRangeIterator; +import org.alfresco.jcr.util.JCRProxyFactory; +import org.alfresco.service.cmr.repository.NodeRef; + + +/** + * Alfresco implementation of a Node Iterator + * + * @author David Caruana + */ +public class NodeRefNodeIteratorImpl extends AbstractRangeIterator + implements NodeIterator +{ + private SessionImpl sessionImpl; + private List nodeRefs; + + /** + * Construct + * + * @param context session context + * @param nodes node list + */ + public NodeRefNodeIteratorImpl(SessionImpl sessionImpl, List nodeRefs) + { + this.sessionImpl = sessionImpl; + this.nodeRefs = nodeRefs; + } + + /* (non-Javadoc) + * @see javax.jcr.NodeIterator#nextNode() + */ + public Node nextNode() + { + long position = skip(); + NodeRef nodeRef = nodeRefs.get((int)position); + NodeImpl nodeImpl = new NodeImpl(sessionImpl, nodeRef); + return nodeImpl.getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.RangeIterator#getSize() + */ + public long getSize() + { + return nodeRefs.size(); + } + + /* (non-Javadoc) + * @see java.util.Iterator#next() + */ + public Object next() + { + return nextNode(); + } + +} diff --git a/source/java/org/alfresco/jcr/item/PropertyImpl.java b/source/java/org/alfresco/jcr/item/PropertyImpl.java new file mode 100644 index 0000000000..c3bbc1454a --- /dev/null +++ b/source/java/org/alfresco/jcr/item/PropertyImpl.java @@ -0,0 +1,684 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.item; + +import java.io.InputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.List; + +import javax.jcr.AccessDeniedException; +import javax.jcr.Item; +import javax.jcr.ItemNotFoundException; +import javax.jcr.ItemVisitor; +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.Value; +import javax.jcr.ValueFormatException; +import javax.jcr.lock.LockException; +import javax.jcr.nodetype.ConstraintViolationException; +import javax.jcr.nodetype.PropertyDefinition; +import javax.jcr.version.VersionException; + +import org.alfresco.jcr.api.JCRNodeRef; +import org.alfresco.jcr.dictionary.DataTypeMap; +import org.alfresco.jcr.dictionary.PropertyDefinitionImpl; +import org.alfresco.jcr.util.JCRProxyFactory; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.InvalidTypeException; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.repository.datatype.TypeConversionException; +import org.alfresco.service.namespace.QName; + + +/** + * Alfresco implementation of a Property + * + * @author David Caruana + */ +public class PropertyImpl extends ItemImpl implements Property +{ + + private NodeImpl node; + private QName name; + private Property proxy = null; + + + /** + * Constructor + * + * @param session + */ + public PropertyImpl(NodeImpl node, QName name) + { + super(node.session); + this.node = node; + this.name = name; + } + + /** + * Create proxied JCR Property + * + * @return property + */ + @Override + public Property getProxy() + { + if (proxy == null) + { + proxy = (Property)JCRProxyFactory.create(this, Property.class, session); + } + return proxy; + } + + /* (non-Javadoc) + * @see javax.jcr.Item#remove() + */ + public void remove() throws VersionException, LockException, ConstraintViolationException, RepositoryException + { + setValue((Value)null); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#setValue(javax.jcr.Value) + */ + public void setValue(Value value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + setPropertyValue(value, -1); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#setValue(javax.jcr.Value[]) + */ + public void setValue(Value[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + setPropertyValue(values, -1); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#setValue(java.lang.String) + */ + public void setValue(String value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + setPropertyValue(value, -1); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#setValue(java.lang.String[]) + */ + public void setValue(String[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + setPropertyValue(values, -1); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#setValue(java.io.InputStream) + */ + public void setValue(InputStream value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + setPropertyValue(value, -1); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#setValue(long) + */ + public void setValue(long value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + setPropertyValue(value, -1); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#setValue(double) + */ + public void setValue(double value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + setPropertyValue(value, -1); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#setValue(java.util.Calendar) + */ + public void setValue(Calendar value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + setPropertyValue((value == null) ? null : value.getTime(), -1); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#setValue(boolean) + */ + public void setValue(boolean value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + setPropertyValue(value, -1); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#setValue(javax.jcr.Node) + */ + public void setValue(Node value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + setPropertyValue((value == null) ? null : JCRNodeRef.getNodeRef(value), -1); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#getValue() + */ + public Value getValue() throws ValueFormatException, RepositoryException + { + checkSingleValued(); + ValueImpl valueImpl = new ValueImpl(session, getType(), getPropertyValue()); + // TODO: Could consider returning proxied value implementation (but i don't think is necessary) + return valueImpl; + } + + /* (non-Javadoc) + * @see javax.jcr.Property#getValues() + */ + public Value[] getValues() throws ValueFormatException, RepositoryException + { + // get values from node property + checkMultiValued(); + Collection values = (Collection)getPropertyValue(); + int type = getType(); + + // construct JCR wrappers + List jcrValues = new ArrayList(values.size()); + for (Object value : values) + { + // Note: In JCR all null values are stripped + if (value != null) + { + // TODO: Could consider returning proxied value implementation (but i don't think is necessary) + jcrValues.add(new ValueImpl(session, type, value)); + } + } + + return jcrValues.toArray(new Value[jcrValues.size()]); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#getString() + */ + public String getString() throws ValueFormatException, RepositoryException + { + checkSingleValued(); + return session.getTypeConverter().stringValue(getPropertyValue()); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#getStream() + */ + public InputStream getStream() throws ValueFormatException, RepositoryException + { + checkSingleValued(); + return session.getTypeConverter().streamValue(getPropertyValue()); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#getLong() + */ + public long getLong() throws ValueFormatException, RepositoryException + { + checkSingleValued(); + return session.getTypeConverter().longValue(getPropertyValue()); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#getDouble() + */ + public double getDouble() throws ValueFormatException, RepositoryException + { + checkSingleValued(); + return session.getTypeConverter().doubleValue(getPropertyValue()); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#getDate() + */ + public Calendar getDate() throws ValueFormatException, RepositoryException + { + checkSingleValued(); + return session.getTypeConverter().dateValue(getPropertyValue()); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#getBoolean() + */ + public boolean getBoolean() throws ValueFormatException, RepositoryException + { + checkSingleValued(); + return session.getTypeConverter().booleanValue(getPropertyValue()); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#getNode() + */ + public Node getNode() throws ValueFormatException, RepositoryException + { + checkSingleValued(); + return session.getTypeConverter().referenceValue(getPropertyValue()).getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#getLength() + */ + public long getLength() throws ValueFormatException, RepositoryException + { + checkSingleValued(); + return getPropertyLength(getPropertyValue()); + } + + /* (non-Javadoc) + * @see javax.jcr.Property#getLengths() + */ + public long[] getLengths() throws ValueFormatException, RepositoryException + { + checkMultiValued(); + Collection values = (Collection)getPropertyValue(); + long[] lengths = new long[values.size()]; + int i = 0; + for (Object value : values) + { + lengths[i++] = getPropertyLength(value); + } + return lengths; + } + + /* (non-Javadoc) + * @see javax.jcr.Property#getDefinition() + */ + public PropertyDefinition getDefinition() throws RepositoryException + { + PropertyDefinitionImpl propDefImpl = new PropertyDefinitionImpl(session.getTypeManager(), getPropertyDefinition()); + return propDefImpl; + } + + /* (non-Javadoc) + * @see javax.jcr.Property#getType() + */ + public int getType() throws RepositoryException + { + // TODO: The type should be based on the property value (in the case of undefined required type) + return DataTypeMap.convertDataTypeToPropertyType(getPropertyDefinition().getDataType().getName()); + } + + /* (non-Javadoc) + * @see javax.jcr.Item#getName() + */ + public String getName() throws RepositoryException + { + return name.toPrefixString(session.getNamespaceResolver()); + } + + /* (non-Javadoc) + * @see javax.jcr.Item#isNode() + */ + public boolean isNode() + { + return false; + } + + /* (non-Javadoc) + * @see javax.jcr.Item#getParent() + */ + public Node getParent() throws ItemNotFoundException, AccessDeniedException, RepositoryException + { + return node.getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.Item#getPath() + */ + public String getPath() throws RepositoryException + { + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + Path path = nodeService.getPath(node.getNodeRef()); + path.append(new JCRPath.SimpleElement(name)); + return path.toPrefixString(session.getNamespaceResolver()); + } + + /* (non-Javadoc) + * @see javax.jcr.Item#getDepth() + */ + public int getDepth() throws RepositoryException + { + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + Path path = nodeService.getPath(node.getNodeRef()); + // Note: Property is one depth lower than its node + return path.size(); + } + + /* (non-Javadoc) + * @see javax.jcr.Item#getAncestor(int) + */ + public Item getAncestor(int depth) throws ItemNotFoundException, AccessDeniedException, RepositoryException + { + int propertyDepth = getDepth(); + if (depth < 0 || depth > propertyDepth) + { + throw new ItemNotFoundException("Ancestor at depth " + depth + " not found for property " + name); + } + + if (depth == propertyDepth) + { + return this.getProxy(); + } + else + { + return node.getAncestor(depth -1); + } + } + + /* (non-Javadoc) + * @see javax.jcr.Item#isSame(javax.jcr.Item) + */ + public boolean isSame(Item otherItem) throws RepositoryException + { + return getProxy().equals(otherItem); + } + + /* (non-Javadoc) + * @see javax.jcr.Item#accept(javax.jcr.ItemVisitor) + */ + public void accept(ItemVisitor visitor) throws RepositoryException + { + visitor.visit(getProxy()); + } + + /** + * Gets the Node Implementation that contains this property + * + * @return the node implementation + */ + protected NodeImpl getNodeImpl() + { + return node; + } + + /** + * Gets the Property Name + * + * @return the property name + */ + protected QName getPropertyName() + { + return name; + } + + /** + * Gets the property value + * + * @return the property value + */ + protected Object getPropertyValue() + throws RepositoryException + { + Object value = null; + + if (getPropertyDefinition().getDataType().getName().equals(DataTypeDefinition.CONTENT)) + { + // Retrieve content reader as value + ContentService contentService = node.session.getRepositoryImpl().getServiceRegistry().getContentService(); + value = contentService.getReader(node.getNodeRef(), name); + if (value == null) + { + // TODO: Check - If value is now null, then effectively the property has been removed + throw new RepositoryException("Property " + name + " has been removed."); + } + } + else + { + // TODO: We may need to copy value here... + NodeService nodeService = node.session.getRepositoryImpl().getServiceRegistry().getNodeService(); + value = nodeService.getProperty(node.getNodeRef(), name); + if (value == null) + { + // TODO: Check - If value is now null, then effectively the property has been removed + throw new RepositoryException("Property " + name + " has been removed."); + } + + // Note: Internal check to ensure that value is single or multi-valued as expected + boolean multiValued = getPropertyDefinition().isMultiValued(); + if (multiValued != (value instanceof Collection)) + { + throw new RepositoryException("Alfresco value does not match multi-valued definition of " + multiValued); + } + } + + return value; + } + + /** + * Get Length of a Value + * + * @param value + * @return + * @throws ValueFormatException + * @throws RepositoryException + */ + private long getPropertyLength(Object value) throws ValueFormatException, RepositoryException + { + // Handle streams + if (value instanceof ContentReader) + { + return ((ContentReader)value).getSize(); + } + if (value instanceof InputStream) + { + return -1; + } + + // Handle all other data types by converting to string + String strValue = (String)DefaultTypeConverter.INSTANCE.convert(String.class, value); + return strValue.length(); + } + + /** + * Sets a property value + * + * @param value the value to set + * @param type type to explicitly convert to or -1 to convert to property type + * @throws RepositoryException + */ + protected void setPropertyValue(Object value, int type) + throws RepositoryException + { + checkSingleValued(); + Object castValue = castValue(value, type); + writeValue(castValue); + } + + /** + * Sets a property value + * + * @param values the values to set + * @param type type to explicitly convert to or -1 to convert to property type + * @throws RepositoryException + */ + protected void setPropertyValue(Object[] values, int type) + throws RepositoryException + { + checkMultiValued(); + + // create collection for multi-valued property + List castValues = null; + if (values != null) + { + castValues = new ArrayList(values.length); + for (Object value : values) + { + Object castValue = castValue(value, type); + castValues.add(castValue); + } + } + + writeValue(castValues); + } + + /** + * Cast value to appropriate type for this property + * + * @param value value to cast + * @param type -1 => cast to property type; otherwise cast to type explicitly provided + * @return the cast value + * @throws RepositoryException + */ + private Object castValue(Object value, int type) + throws RepositoryException + { + // extract raw value if JCR value provided + if (value instanceof Value) + { + value = ValueImpl.getValue((Value)value); + } + + // cast value to appropriate type + DataTypeDefinition dataTypeDef = getPropertyDefinition().getDataType(); + if (type != -1 && dataTypeDef.getName().equals(DataTypeDefinition.ANY)) + { + // attempt cast to explicitly specified type, but only in case where property type can be ANY + QName dataTypeName = DataTypeMap.convertPropertyTypeToDataType(type); + DictionaryService dictionaryService = session.getRepositoryImpl().getServiceRegistry().getDictionaryService(); + dataTypeDef = dictionaryService.getDataType(dataTypeName); + if (!dataTypeName.equals(DataTypeDefinition.CONTENT)) + { + value = session.getTypeConverter().convert(dataTypeDef, value); + } + } + + // special case where binary is converted to inputStream ready for writing via a ContentWriter + if (dataTypeDef.getName().equals(DataTypeDefinition.CONTENT)) + { + value = session.getTypeConverter().streamValue(value); + } + + return value; + } + + /** + * Write the passed value to the property + * + * @param value value to write + * @throws ValueFormatException + */ + private void writeValue(Object value) + throws ValueFormatException + { + // set the property value + if (value instanceof InputStream) + { + // write content + try + { + ContentService contentService = session.getRepositoryImpl().getServiceRegistry().getContentService(); + ContentWriter writer = contentService.getWriter(node.getNodeRef(), name, true); + writer.setMimetype(MimetypeMap.MIMETYPE_BINARY); + writer.putContent((InputStream)value); + } + catch(InvalidTypeException e) + { + throw new ValueFormatException(e); + } + } + else + { + // write property value + // Note: In the case of Content properties, this effectively "deletes" the content when the value is null + try + { + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + nodeService.setProperty(node.getNodeRef(), name, (Serializable)value); + } + catch(TypeConversionException e) + { + throw new ValueFormatException(e); + } + } + } + + /** + * Checks that this property is single valued. + * + * @throws ValueFormatException if value is multi-valued + */ + private void checkSingleValued() + throws ValueFormatException + { + if (getPropertyDefinition().isMultiValued()) + { + // Expected exception for JSR-170 + throw new ValueFormatException("Property " + name + " is multi-valued."); + } + } + + /** + * Checks that this property is single valued. + * + * @throws ValueFormatException if value is multi-valued + */ + private void checkMultiValued() + throws ValueFormatException + { + if (!getPropertyDefinition().isMultiValued()) + { + // Expected exception for JSR-170 + throw new ValueFormatException("Property " + name + " is single-valued."); + } + } + + /** + * Gets the Property Data Type + * + * @return the (JCR) data type + */ + private org.alfresco.service.cmr.dictionary.PropertyDefinition getPropertyDefinition() + { + DictionaryService dictionary = session.getRepositoryImpl().getServiceRegistry().getDictionaryService(); + return dictionary.getProperty(name); + } + + @Override + public boolean equals(Object obj) + { + if (obj == this) + { + return true; + } + if (!(obj instanceof PropertyImpl)) + { + return false; + } + PropertyImpl other = (PropertyImpl)obj; + return this.name.equals(other.name); + } + + @Override + public int hashCode() + { + return name.hashCode(); + } + +} diff --git a/source/java/org/alfresco/jcr/item/PropertyListIterator.java b/source/java/org/alfresco/jcr/item/PropertyListIterator.java new file mode 100644 index 0000000000..21967cc6aa --- /dev/null +++ b/source/java/org/alfresco/jcr/item/PropertyListIterator.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.item; + +import java.util.List; + +import javax.jcr.Property; +import javax.jcr.PropertyIterator; + +import org.alfresco.jcr.util.AbstractRangeIterator; + + +/** + * Alfresco implementation of a Property Iterator + * + * @author David Caruana + */ +public class PropertyListIterator extends AbstractRangeIterator + implements PropertyIterator +{ + private List properties; + + + /** + * Construct + * + * @param context session context + * @param properties property list + */ + public PropertyListIterator(List properties) + { + this.properties = properties; + } + + /* (non-Javadoc) + * @see javax.jcr.PropertyIterator#nextProperty() + */ + public Property nextProperty() + { + long position = skip(); + PropertyImpl propertyImpl = properties.get((int)position); + return propertyImpl.getProxy(); + } + + + /* (non-Javadoc) + * @see javax.jcr.RangeIterator#getSize() + */ + public long getSize() + { + return properties.size(); + } + + /* (non-Javadoc) + * @see java.util.Iterator#next() + */ + public Object next() + { + return nextProperty(); + } + +} diff --git a/source/java/org/alfresco/jcr/item/ValueFactoryImpl.java b/source/java/org/alfresco/jcr/item/ValueFactoryImpl.java new file mode 100644 index 0000000000..5645daa2d1 --- /dev/null +++ b/source/java/org/alfresco/jcr/item/ValueFactoryImpl.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.item; + +import java.io.InputStream; +import java.util.Calendar; +import java.util.Date; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; +import javax.jcr.ValueFactory; +import javax.jcr.ValueFormatException; + +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.jcr.util.JCRProxyFactory; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; + +/** + * Alfresco implementation of JCR Value Factory + * + * @author David Caruana + * + */ +public class ValueFactoryImpl implements ValueFactory +{ + private SessionImpl session; + private ValueFactory proxy = null; + + /** + * Construct + * + * @param session + */ + public ValueFactoryImpl(SessionImpl session) + { + this.session = session; + } + + /** + * Get proxied JCR Value Factory + * + * @return + */ + public ValueFactory getProxy() + { + if (proxy == null) + { + proxy = (ValueFactory)JCRProxyFactory.create(this, ValueFactory.class, session); + } + return proxy; + } + + /* (non-Javadoc) + * @see javax.jcr.ValueFactory#createValue(java.lang.String, int) + */ + public Value createValue(String value, int type) throws ValueFormatException + { + Value createdValue = null; + + try + { + switch(type) + { + case PropertyType.STRING: + createdValue = createValue(session.getTypeConverter().stringValue(value)); + break; + case PropertyType.LONG: + createdValue = createValue(session.getTypeConverter().longValue(value)); + break; + case PropertyType.DOUBLE: + createdValue = createValue(session.getTypeConverter().doubleValue(value)); + break; + case PropertyType.BOOLEAN: + createdValue = createValue(session.getTypeConverter().booleanValue(value)); + break; + case PropertyType.DATE: + createdValue = new ValueImpl(session, PropertyType.DATE, session.getTypeConverter().convert(Date.class, value)); + break; + case PropertyType.BINARY: + createdValue = createValue(session.getTypeConverter().streamValue(value)); + break; + case PropertyType.REFERENCE: + createdValue = new ValueImpl(session, PropertyType.REFERENCE, session.getTypeConverter().referenceValue(value)); + break; + case PropertyType.NAME: + QName name = session.getTypeConverter().convert(QName.class, value); + createdValue = new ValueImpl(session, PropertyType.NAME, name); + break; + case PropertyType.PATH: + Path path = session.getTypeConverter().convert(Path.class, value); + createdValue = new ValueImpl(session, PropertyType.PATH, path); + break; + default: + throw new ValueFormatException("Cannot create value of type " + type); + } + } + catch(RepositoryException e) + { + // Should this method also throw repository exception + throw new ValueFormatException("Failed to create value " + value + " of type " + type, e); + } + return createdValue; + } + + /* (non-Javadoc) + * @see javax.jcr.ValueFactory#createValue(java.lang.String) + */ + public Value createValue(String value) + { + return new ValueImpl(session, PropertyType.STRING, value); + } + + /* (non-Javadoc) + * @see javax.jcr.ValueFactory#createValue(long) + */ + public Value createValue(long value) + { + return new ValueImpl(session, PropertyType.LONG, value); + } + + /* (non-Javadoc) + * @see javax.jcr.ValueFactory#createValue(double) + */ + public Value createValue(double value) + { + return new ValueImpl(session, PropertyType.DOUBLE, value); + } + + /* (non-Javadoc) + * @see javax.jcr.ValueFactory#createValue(boolean) + */ + public Value createValue(boolean value) + { + return new ValueImpl(session, PropertyType.BOOLEAN, value); + } + + /* (non-Javadoc) + * @see javax.jcr.ValueFactory#createValue(java.util.Calendar) + */ + public Value createValue(Calendar value) + { + return new ValueImpl(session, PropertyType.DATE, value.getTime()); + } + + /* (non-Javadoc) + * @see javax.jcr.ValueFactory#createValue(java.io.InputStream) + */ + public Value createValue(InputStream value) + { + return new ValueImpl(session, PropertyType.BINARY, value); + } + + /* (non-Javadoc) + * @see javax.jcr.ValueFactory#createValue(javax.jcr.Node) + */ + public Value createValue(Node value) throws RepositoryException + { + if (value == null) + { + throw new RepositoryException("Node value must not be null"); + } + + // TODO: refer to ContentModel Constants + Property protocol = value.getProperty("sys:store-protocol"); + Property identifier = value.getProperty("sys:store-identifier"); + Property uuid = value.getProperty("sys:node-uuid"); + + // construct a node reference + NodeRef ref = new NodeRef(new StoreRef(protocol.getString(), identifier.getString()), uuid.getString()); + return new ValueImpl(session, PropertyType.REFERENCE, ref); + } + +} diff --git a/source/java/org/alfresco/jcr/item/ValueImpl.java b/source/java/org/alfresco/jcr/item/ValueImpl.java new file mode 100644 index 0000000000..5d28d83a4b --- /dev/null +++ b/source/java/org/alfresco/jcr/item/ValueImpl.java @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.item; + +import java.io.InputStream; +import java.util.Calendar; + +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; +import javax.jcr.ValueFormatException; + +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.jcr.util.JCRProxyFactory; +import org.alfresco.service.cmr.repository.ContentReader; + + +/** + * Alfresco implementation of JCR Value + * + * @author David Caruana + */ +public class ValueImpl implements Value +{ + private enum ValueState {Stream, Value, None}; + private ValueState state = ValueState.None; + + private SessionImpl session; + private int datatype; + private Object value; + private InputStream stream = null; + + private Value proxy; + + + /** + * Constuct + * + * @param value value to wrap + */ + public ValueImpl(SessionImpl session, int datatype, Object value) + { + this.session = session; + this.datatype = datatype; + this.value = value; + } + + /** + * Create a proxied JCR Value + * + * @return the proxied value + */ + public Value getProxy() + { + if (proxy == null) + { + proxy = (Value)JCRProxyFactory.create(this, Value.class, session); + } + return proxy; + } + + /* (non-Javadoc) + * @see javax.jcr.Value#getString() + */ + public String getString() throws ValueFormatException, IllegalStateException, RepositoryException + { + isValidState(ValueState.Value); + String typedValue = session.getTypeConverter().stringValue(getInternalValue()); + value = typedValue; + enterState(ValueState.Value); + return typedValue; + } + + /* (non-Javadoc) + * @see javax.jcr.Value#getStream() + */ + public InputStream getStream() throws IllegalStateException, RepositoryException + { + isValidState(ValueState.Stream); + if (stream == null) + { + stream = session.getTypeConverter().streamValue(value); + } + enterState(ValueState.Stream); + return stream; + } + + /* (non-Javadoc) + * @see javax.jcr.Value#getLong() + */ + public long getLong() throws ValueFormatException, IllegalStateException, RepositoryException + { + isValidState(ValueState.Value); + long typedValue = session.getTypeConverter().longValue(getInternalValue()); + value = typedValue; + enterState(ValueState.Value); + return typedValue; + } + + /* (non-Javadoc) + * @see javax.jcr.Value#getDouble() + */ + public double getDouble() throws ValueFormatException, IllegalStateException, RepositoryException + { + isValidState(ValueState.Value); + double typedValue = session.getTypeConverter().doubleValue(getInternalValue()); + value = typedValue; + enterState(ValueState.Value); + return typedValue; + } + + /* (non-Javadoc) + * @see javax.jcr.Value#getDate() + */ + public Calendar getDate() throws ValueFormatException, IllegalStateException, RepositoryException + { + isValidState(ValueState.Value); + Calendar typedValue = session.getTypeConverter().dateValue(getInternalValue()); + value = typedValue.getTime(); + enterState(ValueState.Value); + return typedValue; + } + + /* (non-Javadoc) + * @see javax.jcr.Value#getBoolean() + */ + public boolean getBoolean() throws ValueFormatException, IllegalStateException, RepositoryException + { + isValidState(ValueState.Value); + boolean typedValue = session.getTypeConverter().booleanValue(getInternalValue()); + value = typedValue; + enterState(ValueState.Value); + return typedValue; + } + + /* (non-Javadoc) + * @see javax.jcr.Value#getType() + */ + public int getType() + { + return datatype; + } + + /** + * Get value + * + * @param value the value wrapper to extract from + * @return the value + */ + public static Object getValue(Value value) throws RepositoryException + { + Object objValue = null; + int valueType = value.getType(); + + switch(valueType) + { + case PropertyType.STRING: + case PropertyType.NAME: + case PropertyType.PATH: + objValue = value.getString(); + break; + case PropertyType.LONG: + objValue = value.getLong(); + break; + case PropertyType.DOUBLE: + objValue = value.getDouble(); + break; + case PropertyType.BOOLEAN: + objValue = value.getBoolean(); + break; + case PropertyType.DATE: + objValue = value.getDate(); + break; + case PropertyType.BINARY: + objValue = value.getStream(); + break; + default: + // Note: just take the internal value + objValue = ((ValueImpl)value).value; + break; + } + + return objValue; + } + + /** + * Get typed value + * + * @param value the value to extract from + * @return the wrapped object + */ + public static Object getValue(JCRTypeConverter typeConverter, int requiredType, Value value) throws RepositoryException + { + Object objValue = null; + + switch(requiredType) + { + case PropertyType.STRING: + objValue = value.getString(); + break; + case PropertyType.LONG: + objValue = value.getLong(); + break; + case PropertyType.DOUBLE: + objValue = value.getDouble(); + break; + case PropertyType.BOOLEAN: + objValue = value.getBoolean(); + break; + case PropertyType.DATE: + objValue = value.getDate(); + break; + case PropertyType.BINARY: + objValue = value.getStream(); + break; + case PropertyType.NAME: + objValue = typeConverter.nameValue(ValueImpl.getValue(value)); + break; + case PropertyType.PATH: + objValue = typeConverter.pathValue(ValueImpl.getValue(value)); + break; + default: + throw new ValueFormatException("Unsupported Value Type " + requiredType); + } + + return objValue; + } + + /** + * Retrieve Value + * + * Note: When retrieving non stream values against a backed stream, the content reader + * has to be re-created. + * + * @return the value + */ + private Object getInternalValue() + { + if (value instanceof ContentReader && state == ValueState.Value) + { + value = ((ContentReader)value).getReader(); + } + return value; + } + + /** + * Check for valid state + * + * @param state the state to check + * @throws IllegalStateException state is not valid + */ + private void isValidState(ValueState state) + { + if (this.state != ValueState.None && this.state != state) + { + throw new IllegalStateException("This value has already been retrieved as a " + state + " and cannot be retrieved as a " + ValueState.Stream + "."); + } + } + + /** + * Enter state + * + * @param state the state to enter + */ + private void enterState(ValueState state) + { + this.state = state; + } + + @Override + public boolean equals(Object obj) + { + if (obj == this) + { + return true; + } + if (!(obj instanceof ValueImpl)) + { + return false; + } + ValueImpl other = (ValueImpl)obj; + + // check data type first + if (datatype != other.datatype) + { + return false; + } + + // handle case where values are content streams + if (value instanceof ContentReader) + { + String thisUrl = ((ContentReader)value).getContentUrl(); + String otherUrl = ((ContentReader)other).getContentUrl(); + return thisUrl.equals(otherUrl); + } + + // handle other value types + return value.equals(other.value); + } + + @Override + public int hashCode() + { + return value.hashCode() * 32 + datatype; + } + +} diff --git a/source/java/org/alfresco/jcr/item/property/JCRLockIsDeepProperty.java b/source/java/org/alfresco/jcr/item/property/JCRLockIsDeepProperty.java new file mode 100644 index 0000000000..89e680b88e --- /dev/null +++ b/source/java/org/alfresco/jcr/item/property/JCRLockIsDeepProperty.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.item.property; + +import javax.jcr.RepositoryException; + +import org.alfresco.jcr.dictionary.JCRNamespace; +import org.alfresco.jcr.item.NodeImpl; +import org.alfresco.jcr.item.PropertyImpl; +import org.alfresco.service.namespace.QName; + +/** + * Implementation for mix:lockable lockIsDeep property + * + * @author David Caruana + */ +public class JCRLockIsDeepProperty extends PropertyImpl +{ + public static QName PROPERTY_NAME = QName.createQName(JCRNamespace.JCR_URI, "lockIsDeep"); + + + /** + * Construct + * + * @param node + */ + public JCRLockIsDeepProperty(NodeImpl node) + { + super(node, PROPERTY_NAME); + } + + @Override + protected Object getPropertyValue() throws RepositoryException + { + return false; + } + +} diff --git a/source/java/org/alfresco/jcr/item/property/JCRLockOwnerProperty.java b/source/java/org/alfresco/jcr/item/property/JCRLockOwnerProperty.java new file mode 100644 index 0000000000..007945872f --- /dev/null +++ b/source/java/org/alfresco/jcr/item/property/JCRLockOwnerProperty.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.item.property; + +import javax.jcr.RepositoryException; + +import org.alfresco.jcr.dictionary.JCRNamespace; +import org.alfresco.jcr.item.NodeImpl; +import org.alfresco.jcr.item.PropertyImpl; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + +/** + * Implementation for mix:lockable lockOwner property + * + * @author David Caruana + */ +public class JCRLockOwnerProperty extends PropertyImpl +{ + public static QName PROPERTY_NAME = QName.createQName(JCRNamespace.JCR_URI, "lockOwner"); + + + /** + * Construct + * + * @param node + */ + public JCRLockOwnerProperty(NodeImpl node) + { + super(node, PROPERTY_NAME); + } + + @Override + protected Object getPropertyValue() throws RepositoryException + { + NodeImpl nodeImpl = getNodeImpl(); + NodeService nodeService = nodeImpl.getSessionImpl().getRepositoryImpl().getServiceRegistry().getNodeService(); + String lockOwner = (String)nodeService.getProperty(nodeImpl.getNodeRef(), ContentModel.PROP_LOCK_OWNER); + return lockOwner; + } + +} diff --git a/source/java/org/alfresco/jcr/item/property/JCRMixinTypesProperty.java b/source/java/org/alfresco/jcr/item/property/JCRMixinTypesProperty.java new file mode 100644 index 0000000000..09a45b7e4a --- /dev/null +++ b/source/java/org/alfresco/jcr/item/property/JCRMixinTypesProperty.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.item.property; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import javax.jcr.RepositoryException; + +import org.alfresco.jcr.dictionary.JCRNamespace; +import org.alfresco.jcr.dictionary.NodeTypeImpl; +import org.alfresco.jcr.item.NodeImpl; +import org.alfresco.jcr.item.PropertyImpl; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + +/** + * Implementation for nt:base primaryType property + * + * @author David Caruana + */ +public class JCRMixinTypesProperty extends PropertyImpl +{ + public static QName PROPERTY_NAME = QName.createQName(JCRNamespace.JCR_URI, "mixinTypes"); + + + /** + * Construct + * + * @param node + */ + public JCRMixinTypesProperty(NodeImpl node) + { + super(node, PROPERTY_NAME); + } + + @Override + protected Object getPropertyValue() throws RepositoryException + { + // get aspects from node + NodeImpl nodeImpl = getNodeImpl(); + NodeService nodeService = nodeImpl.getSessionImpl().getRepositoryImpl().getServiceRegistry().getNodeService(); + Set aspects = nodeService.getAspects(nodeImpl.getNodeRef()); + + // resolve against session namespace prefix resolver + List aspectNames = new ArrayList(aspects.size() + 1); + for (QName aspect : aspects) + { + aspectNames.add(aspect.toPrefixString(nodeImpl.getSessionImpl().getNamespaceResolver())); + } + + // add JCR referenceable + aspectNames.add(NodeTypeImpl.MIX_REFERENCEABLE.toPrefixString(nodeImpl.getSessionImpl().getNamespaceResolver())); + + return aspectNames; + } + +} diff --git a/source/java/org/alfresco/jcr/item/property/JCRPrimaryTypeProperty.java b/source/java/org/alfresco/jcr/item/property/JCRPrimaryTypeProperty.java new file mode 100644 index 0000000000..ffedb0a4ce --- /dev/null +++ b/source/java/org/alfresco/jcr/item/property/JCRPrimaryTypeProperty.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.item.property; + +import javax.jcr.RepositoryException; + +import org.alfresco.jcr.dictionary.JCRNamespace; +import org.alfresco.jcr.item.NodeImpl; +import org.alfresco.jcr.item.PropertyImpl; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + +/** + * Implementation for nt:base primaryType property + * + * @author David Caruana + */ +public class JCRPrimaryTypeProperty extends PropertyImpl +{ + public static QName PROPERTY_NAME = QName.createQName(JCRNamespace.JCR_URI, "primaryType"); + + + /** + * Construct + * + * @param node + */ + public JCRPrimaryTypeProperty(NodeImpl node) + { + super(node, PROPERTY_NAME); + } + + @Override + protected Object getPropertyValue() throws RepositoryException + { + NodeImpl nodeImpl = getNodeImpl(); + NodeService nodeService = nodeImpl.getSessionImpl().getRepositoryImpl().getServiceRegistry().getNodeService(); + QName type = nodeService.getType(nodeImpl.getNodeRef()); + return type.toPrefixString(nodeImpl.getSessionImpl().getNamespaceResolver()); + } + +} diff --git a/source/java/org/alfresco/jcr/item/property/JCRUUIDProperty.java b/source/java/org/alfresco/jcr/item/property/JCRUUIDProperty.java new file mode 100644 index 0000000000..4133b5a917 --- /dev/null +++ b/source/java/org/alfresco/jcr/item/property/JCRUUIDProperty.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.item.property; + +import javax.jcr.RepositoryException; + +import org.alfresco.jcr.dictionary.JCRNamespace; +import org.alfresco.jcr.item.NodeImpl; +import org.alfresco.jcr.item.PropertyImpl; +import org.alfresco.service.namespace.QName; + + +/** + * Implementation for mix:referenceable uuid property + + * @author David Caruana + * + */ +public class JCRUUIDProperty extends PropertyImpl +{ + public static QName PROPERTY_NAME = QName.createQName(JCRNamespace.JCR_URI, "uuid"); + + /** + * Construct + * + * @param node + */ + public JCRUUIDProperty(NodeImpl node) + { + super(node, PROPERTY_NAME); + } + + @Override + protected Object getPropertyValue() throws RepositoryException + { + NodeImpl node = getNodeImpl(); + return node.getNodeRef().getId(); + } + +} diff --git a/source/java/org/alfresco/jcr/item/property/PropertyResolver.java b/source/java/org/alfresco/jcr/item/property/PropertyResolver.java new file mode 100644 index 0000000000..83152eea9e --- /dev/null +++ b/source/java/org/alfresco/jcr/item/property/PropertyResolver.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.item.property; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.jcr.PathNotFoundException; + +import org.alfresco.jcr.item.NodeImpl; +import org.alfresco.jcr.item.PropertyImpl; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.QNamePattern; + + +/** + * Responsible for resolving properties on Nodes + * + * @author David Caruana + */ +public class PropertyResolver +{ + + private static Map virtualProperties = new HashMap(); + static + { + virtualProperties.put(JCRUUIDProperty.PROPERTY_NAME, null); + virtualProperties.put(JCRPrimaryTypeProperty.PROPERTY_NAME, null); + virtualProperties.put(JCRMixinTypesProperty.PROPERTY_NAME, null); + virtualProperties.put(JCRLockOwnerProperty.PROPERTY_NAME, ContentModel.ASPECT_LOCKABLE); + virtualProperties.put(JCRLockIsDeepProperty.PROPERTY_NAME, ContentModel.ASPECT_LOCKABLE); + + // TODO: mix:versionable + } + + + /** + * Create Property List for all properties of this node + * + * @return list of properties (null properties are filtered) + */ + public static List createProperties(NodeImpl node, QNamePattern pattern) + { + // Create list of properties from node itself + NodeService nodeService = node.getSessionImpl().getRepositoryImpl().getServiceRegistry().getNodeService(); + Map properties = nodeService.getProperties(node.getNodeRef()); + List propertyList = new ArrayList(properties.size()); + for (Map.Entry entry : properties.entrySet()) + { + QName propertyName = entry.getKey(); + if (pattern == null || pattern.isMatch(propertyName)) + { + Serializable value = entry.getValue(); + if (value != null) + { + PropertyImpl property = new PropertyImpl(node, propertyName); + propertyList.add(property); + } + } + } + + // Add JCR properties + for (Map.Entry virtualProperty : virtualProperties.entrySet()) + { + boolean addJCRProperty = false; + if (virtualProperty.getValue() == null) + { + addJCRProperty = true; + } + else + { + addJCRProperty = nodeService.hasAspect(node.getNodeRef(), virtualProperty.getValue()); + } + + if (addJCRProperty && (pattern == null || pattern.isMatch(virtualProperty.getKey()))) + { + propertyList.add(createVirtualProperty(node, virtualProperty.getKey())); + } + } + + return propertyList; + } + + + /** + * Create property for the given named property + * + * @param node + * @param propertyName + * @return + * @throws PathNotFoundException + */ + public static PropertyImpl createProperty(NodeImpl node, QName propertyName) + throws PathNotFoundException + { + // has a JCR property been requested that is not persisted in Alfresco repository? + if (hasVirtualProperty(node, propertyName)) + { + return createVirtualProperty(node, propertyName); + } + + // has a property been requested that actually exists? + NodeService nodeService = node.getSessionImpl().getRepositoryImpl().getServiceRegistry().getNodeService(); + Serializable value = nodeService.getProperty(node.getNodeRef(), propertyName); + if (value == null) + { + throw new PathNotFoundException("Property path " + propertyName + " not found from node " + node.getNodeRef()); + } + + // construct property wrapper + PropertyImpl propertyImpl = new PropertyImpl(node, propertyName); + return propertyImpl; + } + + + private static PropertyImpl createVirtualProperty(NodeImpl node, QName propertyName) + { + if (propertyName.equals(JCRUUIDProperty.PROPERTY_NAME)) + { + return new JCRUUIDProperty(node); + } + if (propertyName.equals(JCRPrimaryTypeProperty.PROPERTY_NAME)) + { + return new JCRPrimaryTypeProperty(node); + } + if (propertyName.equals(JCRMixinTypesProperty.PROPERTY_NAME)) + { + return new JCRMixinTypesProperty(node); + } + if (propertyName.equals(JCRLockOwnerProperty.PROPERTY_NAME)) + { + return new JCRLockOwnerProperty(node); + } + if (propertyName.equals(JCRLockIsDeepProperty.PROPERTY_NAME)) + { + return new JCRLockIsDeepProperty(node); + } + + return null; + } + + + /** + * Check for existence of Property on specified Node + * + * @param node + * @param propertyName + * @return + */ + public static boolean hasProperty(NodeImpl node, QName propertyName) + { + if (hasVirtualProperty(node, propertyName)) + { + return true; + } + + NodeService nodeService = node.getSessionImpl().getRepositoryImpl().getServiceRegistry().getNodeService(); + Serializable value = nodeService.getProperty(node.getNodeRef(), propertyName); + return value != null; + } + + + private static boolean hasVirtualProperty(NodeImpl node, QName propertyName) + { + // is this a virtual property + if (virtualProperties.containsKey(propertyName)) + { + // is this a virtual property attached to a specific aspect + QName aspect = virtualProperties.get(propertyName); + if (aspect == null) + { + // it's supported on all types + return true; + } + + // is the aspect attached to the node + NodeService nodeService = node.getSessionImpl().getRepositoryImpl().getServiceRegistry().getNodeService(); + return nodeService.hasAspect(node.getNodeRef(), aspect); + } + + // no, it's not even a virtual property + return false; + } + +} diff --git a/source/java/org/alfresco/jcr/query/NodeRefListQueryResultImpl.java b/source/java/org/alfresco/jcr/query/NodeRefListQueryResultImpl.java new file mode 100644 index 0000000000..3ab8e1de82 --- /dev/null +++ b/source/java/org/alfresco/jcr/query/NodeRefListQueryResultImpl.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.query; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; +import javax.jcr.nodetype.NodeType; +import javax.jcr.nodetype.PropertyDefinition; +import javax.jcr.query.Query; +import javax.jcr.query.QueryResult; +import javax.jcr.query.RowIterator; + +import org.alfresco.jcr.item.NodeRefNodeIteratorImpl; +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.jcr.util.JCRProxyFactory; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + + +/** + * Query Result based a NodeRef List + * + * @author David Caruana + */ +public class NodeRefListQueryResultImpl implements QueryResult +{ + /** Session */ + private SessionImpl session; + + /** The node refs in the result set */ + private List nodeRefs; + + /** Node Service */ + private NodeService nodeService; + + /** Column Names */ + private Map columns = null; + + /** Proxy */ + private QueryResult proxy = null; + + + /** + * Construct + * + * @param nodeRefs list of node references + */ + public NodeRefListQueryResultImpl(SessionImpl session, List nodeRefs) + { + this.session = session; + this.nodeRefs = nodeRefs; + this.nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + } + + /** + * Get proxied JCR Query Result + * + * @return proxy + */ + public QueryResult getProxy() + { + if (proxy == null) + { + proxy = (QueryResult)JCRProxyFactory.create(this, QueryResult.class, session); + } + return proxy; + } + + /* (non-Javadoc) + * @see javax.jcr.query.QueryResult#getColumnNames() + */ + public String[] getColumnNames() throws RepositoryException + { + Map columns = getColumnDefinitions(); + String[] names = new String[columns.size()]; + int i = 0; + for (QName columnName : columns.keySet()) + { + names[i++] = columnName.toPrefixString(session.getNamespaceResolver()); + } + return names; + } + + /* (non-Javadoc) + * @see javax.jcr.query.QueryResult#getRows() + */ + public RowIterator getRows() throws RepositoryException + { + return new NodeRefRowIteratorImpl(session, getColumnDefinitions(), nodeRefs).getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.query.QueryResult#getNodes() + */ + public NodeIterator getNodes() throws RepositoryException + { + return new NodeRefNodeIteratorImpl(session, nodeRefs); + } + + + /** + * Get list of column definitions + * + * @return list of column definitions + */ + private Map getColumnDefinitions() + throws RepositoryException + { + if (columns == null) + { + columns = new HashMap(); + + // build list of column names from result set + if (nodeRefs.size() > 0) + { + // Base column list on first node ref + // TODO: determine on a more formal basis + QName type = nodeService.getType(nodeRefs.get(0)); + NodeType nodeType = session.getTypeManager().getNodeType(type.toPrefixString(session.getNamespaceResolver())); + PropertyDefinition[] propDefs = nodeType.getPropertyDefinitions(); + for (PropertyDefinition propDef : propDefs) + { + if (!propDef.isMultiple()) + { + columns.put(QName.createQName(propDef.getName(), session.getNamespaceResolver()), propDef); + } + } + Setaspects = nodeService.getAspects(nodeRefs.get(0)); + for (QName aspect : aspects) + { + NodeType nodeAspect = session.getTypeManager().getNodeType(aspect.toPrefixString(session.getNamespaceResolver())); + propDefs = nodeAspect.getPropertyDefinitions(); + for (PropertyDefinition propDef : propDefs) + { + if (!propDef.isMultiple()) + { + columns.put(QName.createQName(propDef.getName(), session.getNamespaceResolver()), propDef); + } + } + } + } + + // add JCR required columns + columns.put(QueryManagerImpl.JCRPATH_COLUMN, null); + columns.put(QueryManagerImpl.JCRSCORE_COLUMN, null); + } + return columns; + } + +} diff --git a/source/java/org/alfresco/jcr/query/NodeRefRowIteratorImpl.java b/source/java/org/alfresco/jcr/query/NodeRefRowIteratorImpl.java new file mode 100644 index 0000000000..30324d6f76 --- /dev/null +++ b/source/java/org/alfresco/jcr/query/NodeRefRowIteratorImpl.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.query; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +import javax.jcr.nodetype.PropertyDefinition; +import javax.jcr.query.Query; +import javax.jcr.query.Row; +import javax.jcr.query.RowIterator; + +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.jcr.util.AbstractRangeIterator; +import org.alfresco.jcr.util.JCRProxyFactory; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + + +/** + * Row Iterator based on a list of Node References + * + * @author David Caruana + */ +public class NodeRefRowIteratorImpl extends AbstractRangeIterator implements RowIterator +{ + private SessionImpl session; + private Map columns; + private List nodeRefs; + private RowIterator proxy = null; + + /** + * Construct + * + * @param session + * @param columnNames + * @param nodeRefs + */ + public NodeRefRowIteratorImpl(SessionImpl session, Map columns, List nodeRefs) + { + this.session = session; + this.columns = columns; + this.nodeRefs = nodeRefs; + } + + /** + * Get proxied JCR Query + * + * @return proxy + */ + public RowIterator getProxy() + { + if (proxy == null) + { + proxy = (RowIterator)JCRProxyFactory.create(this, RowIterator.class, session); + } + return proxy; + } + + /* (non-Javadoc) + * @see javax.jcr.query.RowIterator#nextRow() + */ + public Row nextRow() + { + long position = skip(); + NodeRef nodeRef = nodeRefs.get((int)position); + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + Map properties = nodeService.getProperties(nodeRef); + return new PropertyMapRowImpl(session, columns, nodeRef, properties); + } + + /* (non-Javadoc) + * @see javax.jcr.RangeIterator#getSize() + */ + public long getSize() + { + return nodeRefs.size(); + } + + /* (non-Javadoc) + * @see java.util.Iterator#next() + */ + public Object next() + { + return nextRow(); + } + +} diff --git a/source/java/org/alfresco/jcr/query/PropertyMapRowImpl.java b/source/java/org/alfresco/jcr/query/PropertyMapRowImpl.java new file mode 100644 index 0000000000..5e151a2201 --- /dev/null +++ b/source/java/org/alfresco/jcr/query/PropertyMapRowImpl.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.query; + +import java.io.Serializable; +import java.util.Map; + +import javax.jcr.ItemNotFoundException; +import javax.jcr.Node; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; +import javax.jcr.nodetype.PropertyDefinition; +import javax.jcr.query.Row; + +import org.alfresco.jcr.item.NodeImpl; +import org.alfresco.jcr.item.ValueImpl; +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + + +/** + * Node Ref based Row + * + * @author David Caruana + */ +public class PropertyMapRowImpl implements Row +{ + private SessionImpl session; + private Map columns; + private NodeRef nodeRef; + private Map properties; + + + /** + * Construct + * + * @param session + * @param columnNames + * @param properties + */ + public PropertyMapRowImpl(SessionImpl session, Map columns, NodeRef nodeRef, Map properties) + { + this.session = session; + this.columns = columns; + this.nodeRef = nodeRef; + this.properties = properties; + } + + /* (non-Javadoc) + * @see javax.jcr.query.Row#getValues() + */ + public Value[] getValues() throws RepositoryException + { + Value[] values = new Value[columns.size() + 2]; + + int i = 0; + for (QName propertyName : columns.keySet()) + { + values[i++] = createValue(propertyName); + } + return values; + } + + /* (non-Javadoc) + * @see javax.jcr.query.Row#getValue(java.lang.String) + */ + public Value getValue(String propertyName) throws ItemNotFoundException, RepositoryException + { + QName propertyQName = QName.createQName(propertyName, session.getNamespaceResolver()); + if (!columns.containsKey(propertyQName)) + { + throw new ItemNotFoundException("Column " + propertyName + " does not exist"); + } + return createValue(propertyQName); + } + + /** + * Create a Value for specified property name + * + * @param propertyName + * @return + * @throws RepositoryException + */ + private Value createValue(QName propertyName) + throws RepositoryException + { + Value value = null; + if (propertyName.equals(QueryManagerImpl.JCRPATH_COLUMN)) + { + // derive path from node ref + Node node = new NodeImpl(session, nodeRef).getProxy(); + value = new ValueImpl(session, PropertyType.STRING, node.getPath()); + } + else if (propertyName.equals(QueryManagerImpl.JCRSCORE_COLUMN)) + { + // TODO: + // create dummy score + value = new ValueImpl(session, PropertyType.LONG, (long)0); + } + else + { + // create value from node properties + Object objValue = properties.get(propertyName); + if (objValue != null) + { + PropertyDefinition propDef = columns.get(propertyName); + value = new ValueImpl(session, propDef.getRequiredType(), objValue); + } + } + return value; + } + +} diff --git a/source/java/org/alfresco/jcr/query/QueryImpl.java b/source/java/org/alfresco/jcr/query/QueryImpl.java new file mode 100644 index 0000000000..dc1145b3a0 --- /dev/null +++ b/source/java/org/alfresco/jcr/query/QueryImpl.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.query; + +import javax.jcr.ItemExistsException; +import javax.jcr.ItemNotFoundException; +import javax.jcr.Node; +import javax.jcr.PathNotFoundException; +import javax.jcr.RepositoryException; +import javax.jcr.UnsupportedRepositoryOperationException; +import javax.jcr.lock.LockException; +import javax.jcr.nodetype.ConstraintViolationException; +import javax.jcr.query.InvalidQueryException; +import javax.jcr.query.Query; +import javax.jcr.version.VersionException; + +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.jcr.util.JCRProxyFactory; + + +/** + * Alfresco implementation of JCR Query + * + * @author David Caruana + */ +public abstract class QueryImpl implements Query +{ + /** Session */ + private SessionImpl session; + + /** Query Statement */ + private String statement; + + /** Proxy */ + private Query proxy = null; + + + /** + * Construct + * + * @param statement query language + */ + public QueryImpl(SessionImpl session, String statement) + { + this.session = session; + this.statement = statement; + } + + /** + * Get proxied JCR Query + * + * @return proxy + */ + public Query getProxy() + { + if (proxy == null) + { + proxy = (Query)JCRProxyFactory.create(this, Query.class, session); + } + return proxy; + } + + /** + * Get Session + * + * @return session + */ + public SessionImpl getSession() + { + return session; + } + + /** + * Is the statement valid? + * + * @throws InvalidQueryException + */ + public abstract void isValidStatement() throws InvalidQueryException; + + /* (non-Javadoc) + * @see javax.jcr.query.Query#getStatement() + */ + public String getStatement() + { + return statement; + } + + /* (non-Javadoc) + * @see javax.jcr.query.Query#getStoredQueryPath() + */ + public String getStoredQueryPath() throws ItemNotFoundException, RepositoryException + { + throw new ItemNotFoundException("This query has not been saved to the Repository"); + } + + /* (non-Javadoc) + * @see javax.jcr.query.Query#storeAsNode(java.lang.String) + */ + public Node storeAsNode(String absPath) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, UnsupportedRepositoryOperationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + +} diff --git a/source/java/org/alfresco/jcr/query/QueryManagerImpl.java b/source/java/org/alfresco/jcr/query/QueryManagerImpl.java new file mode 100644 index 0000000000..b5b31c506c --- /dev/null +++ b/source/java/org/alfresco/jcr/query/QueryManagerImpl.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.query; + +import java.lang.reflect.Constructor; +import java.util.HashMap; +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.query.InvalidQueryException; +import javax.jcr.query.Query; +import javax.jcr.query.QueryManager; + +import org.alfresco.jcr.dictionary.JCRNamespace; +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.service.namespace.QName; + + +/** + * Alfresco implementation of JCR Query Manager + * + * @author David Caruana + */ +public class QueryManagerImpl implements QueryManager +{ + public static QName JCRPATH_COLUMN = QName.createQName(JCRNamespace.JCR_URI, "path"); + public static QName JCRSCORE_COLUMN = QName.createQName(JCRNamespace.JCR_URI, "score"); + + /** supported query languages */ + private static Map> supportedLanguages = new HashMap>(); + static + { + supportedLanguages.put(Query.XPATH, XPathQueryImpl.class); + } + + private SessionImpl session; + + /** + * Construct + * + * @param session session + */ + public QueryManagerImpl(SessionImpl session) + { + this.session = session; + } + + /* (non-Javadoc) + * @see javax.jcr.query.QueryManager#createQuery(java.lang.String, java.lang.String) + */ + public Query createQuery(String statement, String language) throws InvalidQueryException, RepositoryException + { + // is the language known? + if (!isSupportedLanguage(language)) + { + throw new InvalidQueryException("Query language " + language + " is not supported"); + } + + // construct the query + Class queryClass = supportedLanguages.get(language); + try + { + Constructor constructor = queryClass.getConstructor(new Class[] { SessionImpl.class, String.class } ); + QueryImpl queryImpl = constructor.newInstance(new Object[] { session, statement } ); + queryImpl.isValidStatement(); + return queryImpl.getProxy(); + } + catch (InstantiationException e) + { + throw new RepositoryException("Failed to create query " + statement + " (language: " + language + ")"); + } + catch (IllegalAccessException e) + { + throw new RepositoryException("Failed to create query " + statement + " (language: " + language + ")"); + } + catch (Exception e) + { + throw new RepositoryException("Failed to create query " + statement + " (language: " + language + ")"); + } + } + + /* (non-Javadoc) + * @see javax.jcr.query.QueryManager#getQuery(javax.jcr.Node) + */ + public Query getQuery(Node node) throws InvalidQueryException, RepositoryException + { + throw new InvalidQueryException("Persistent queries are not supported by the Repository."); + } + + /* (non-Javadoc) + * @see javax.jcr.query.QueryManager#getSupportedQueryLanguages() + */ + public String[] getSupportedQueryLanguages() throws RepositoryException + { + return supportedLanguages.keySet().toArray(new String[supportedLanguages.size()]); + } + + /** + * Is supported language? + * + * @param language language to check + * @return true => supported + */ + private boolean isSupportedLanguage(String language) + { + return supportedLanguages.containsKey(language); + } + +} diff --git a/source/java/org/alfresco/jcr/query/QueryManagerImplTest.java b/source/java/org/alfresco/jcr/query/QueryManagerImplTest.java new file mode 100644 index 0000000000..23fb325237 --- /dev/null +++ b/source/java/org/alfresco/jcr/query/QueryManagerImplTest.java @@ -0,0 +1,87 @@ +package org.alfresco.jcr.query; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; +import javax.jcr.Value; +import javax.jcr.query.Query; +import javax.jcr.query.QueryManager; +import javax.jcr.query.QueryResult; +import javax.jcr.query.Row; +import javax.jcr.query.RowIterator; + +import org.alfresco.jcr.test.BaseJCRTest; + +public class QueryManagerImplTest extends BaseJCRTest +{ + + protected Session superuserSession; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + SimpleCredentials superuser = new SimpleCredentials("superuser", "".toCharArray()); + superuserSession = repository.login(superuser, getWorkspace()); + } + + @Override + protected void tearDown() throws Exception + { + superuserSession.logout(); + super.tearDown(); + } + + + public void testQuery() + throws Exception + { + QueryManager queryMgr = superuserSession.getWorkspace().getQueryManager(); + String[] languages = queryMgr.getSupportedQueryLanguages(); + assertEquals(1, languages.length); + assertEquals(Query.XPATH, languages[0]); + + Query query = queryMgr.createQuery("//*", Query.XPATH); + QueryResult result = query.execute(); + String[] columnNames = result.getColumnNames(); + + // iterate via row iterator + int rowCnt = 0; + RowIterator rowIterator = result.getRows(); + while(rowIterator.hasNext()) + { + Row row = rowIterator.nextRow(); + for (String columnName : columnNames) + { + Value value = row.getValue(columnName); + if (value != null) + { + String strValue = value.getString(); + assertNotNull(strValue); + } + } + rowCnt++; + } + + // iterate via node iterator + int nodeCnt = 0; + NodeIterator nodeIterator = result.getNodes(); + while(nodeIterator.hasNext()) + { + Node node = nodeIterator.nextNode(); + Property property = node.getProperty("sys:node-uuid"); + Value value = property.getValue(); + String uuid = value.getString(); + assertNotNull(uuid); + nodeCnt++; + } + + // check same number of items returned from each iterator + assertEquals(rowCnt, nodeCnt); + } + + +} diff --git a/source/java/org/alfresco/jcr/query/XPathQueryImpl.java b/source/java/org/alfresco/jcr/query/XPathQueryImpl.java new file mode 100644 index 0000000000..70e4fca857 --- /dev/null +++ b/source/java/org/alfresco/jcr/query/XPathQueryImpl.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.query; + +import java.util.List; + +import javax.jcr.RepositoryException; +import javax.jcr.query.InvalidQueryException; +import javax.jcr.query.Query; +import javax.jcr.query.QueryResult; + +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.search.SearchService; + + +/** + * Alfresco implementation of XPath Query + * + * @author David Caruana + */ +public class XPathQueryImpl extends QueryImpl +{ + + /** + * Construct + * + * @param statement the xpath statement + */ + public XPathQueryImpl(SessionImpl session, String statement) + { + super(session, statement); + } + + @Override + public void isValidStatement() throws InvalidQueryException + { + // TODO + } + + /* (non-Javadoc) + * @see javax.jcr.query.Query#execute() + */ + public QueryResult execute() throws RepositoryException + { + SearchService search = getSession().getRepositoryImpl().getServiceRegistry().getSearchService(); + NodeService nodes = getSession().getRepositoryImpl().getServiceRegistry().getNodeService(); + NodeRef root = nodes.getRootNode(getSession().getWorkspaceStore()); + List nodeRefs = search.selectNodes(root, getStatement(), null, getSession().getNamespaceResolver(), false, SearchService.LANGUAGE_JCR_XPATH); + return new NodeRefListQueryResultImpl(getSession(), nodeRefs).getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.query.Query#getLanguage() + */ + public String getLanguage() + { + return Query.XPATH; + } + +} diff --git a/source/java/org/alfresco/jcr/repository/RepositoryFactory.java b/source/java/org/alfresco/jcr/repository/RepositoryFactory.java new file mode 100644 index 0000000000..aad2e2de7e --- /dev/null +++ b/source/java/org/alfresco/jcr/repository/RepositoryFactory.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.repository; + + +/** + * Factory settings for entry-point Repository implementation + * + * @author David Caruana + */ +public class RepositoryFactory +{ + public final static String APPLICATION_CONTEXT = "classpath:alfresco/jcr-context.xml"; + public final static String REPOSITORY_BEAN = "JCR.Repository"; +} diff --git a/source/java/org/alfresco/jcr/repository/RepositoryImpl.java b/source/java/org/alfresco/jcr/repository/RepositoryImpl.java new file mode 100644 index 0000000000..5b09ad4a9b --- /dev/null +++ b/source/java/org/alfresco/jcr/repository/RepositoryImpl.java @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.repository; + +import java.util.HashMap; +import java.util.Map; + +import javax.jcr.Credentials; +import javax.jcr.LoginException; +import javax.jcr.NoSuchWorkspaceException; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.jcr.dictionary.NamespaceRegistryImpl; +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.repo.importer.ImporterComponent; +import org.alfresco.repo.security.authentication.AuthenticationException; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.descriptor.Descriptor; +import org.alfresco.service.descriptor.DescriptorService; + + +/** + * Alfresco implementation of a JCR Repository + * + * @author David Caruana + */ +public class RepositoryImpl implements Repository +{ + /** Empty Password, if not supplied */ + private final static char[] EMPTY_PASSWORD = "".toCharArray(); + + /** Repository Descriptors */ + private static final Map descriptors = new HashMap(); + + /** Thread Local Session */ + // Note: For now, we're only allowing one active (i.e. logged in) Session per-thread + private static ThreadLocal sessions = new ThreadLocal(); + + // Service dependencies + private ServiceRegistry serviceRegistry; + private ImporterComponent importerComponent; + private String defaultWorkspace = null; + + // Services + private NamespaceRegistryImpl namespaceRegistry = null; + + + // + // Dependency Injection + // + + /** + * Set the service registry + * + * @param serviceRegistry + */ + public void setServiceRegistry(ServiceRegistry serviceRegistry) + { + this.serviceRegistry = serviceRegistry; + } + + /** + * Set the Importer Component + * + * @param importerComponent + */ + public void setImporterComponent(ImporterComponent importerComponent) + { + this.importerComponent = importerComponent; + } + + /** + * Sets the Default Workspace + * + * @param defaultWorkspace default workspace + */ + public void setDefaultWorkspace(String defaultWorkspace) + { + this.defaultWorkspace = defaultWorkspace; + } + + /** + * Initialisation + */ + public void init() + { + if (serviceRegistry == null) + { + throw new IllegalStateException("Service Registry has not been specified."); + } + + // initialise namespace registry + namespaceRegistry = new NamespaceRegistryImpl(false, serviceRegistry.getNamespaceService()); + + // initialise descriptors + DescriptorService descriptorService = serviceRegistry.getDescriptorService(); + Descriptor descriptor = descriptorService.getServerDescriptor(); + + String repNameDesc = "Alfresco Content Repository"; + String edition = descriptor.getEdition(); + if (edition != null && edition.length() > 0) + { + repNameDesc += " (" + edition + ")"; + } + String repVersion = descriptor.getVersion(); + + descriptors.put(Repository.REP_NAME_DESC, repNameDesc); + descriptors.put(Repository.REP_VENDOR_DESC, "Alfresco"); + descriptors.put(Repository.REP_VENDOR_URL_DESC, "http://www.alfresco.org"); + descriptors.put(Repository.REP_VERSION_DESC, repVersion); + descriptors.put(Repository.SPEC_NAME_DESC, "Content Repository API for Java(TM) Technology Specification"); + descriptors.put(Repository.SPEC_VERSION_DESC, "1.0"); + descriptors.put(Repository.LEVEL_1_SUPPORTED, "true"); + descriptors.put(Repository.LEVEL_2_SUPPORTED, "true"); + descriptors.put(Repository.OPTION_TRANSACTIONS_SUPPORTED, "true"); + descriptors.put(Repository.QUERY_XPATH_DOC_ORDER, "true"); + descriptors.put(Repository.QUERY_XPATH_POS_INDEX, "true"); + } + + /** + * Get the service registry + * + * @return the service registry + */ + public ServiceRegistry getServiceRegistry() + { + return serviceRegistry; + } + + /** + * Get the importer component + * + * @return the importer component + */ + public ImporterComponent getImporterComponent() + { + return importerComponent; + } + + /** + * Get the Namespace Registry + */ + public NamespaceRegistryImpl getNamespaceRegistry() + { + return namespaceRegistry; + } + + /* (non-Javadoc) + * @see javax.jcr.Repository#getDescriptorKeys() + */ + public String[] getDescriptorKeys() + { + String[] keys = (String[]) descriptors.keySet().toArray(new String[descriptors.keySet().size()]); + return keys; + } + + /* (non-Javadoc) + * @see javax.jcr.Repository#getDescriptor(java.lang.String) + */ + public String getDescriptor(String key) + { + return descriptors.get(key); + } + + /* (non-Javadoc) + * @see javax.jcr.Repository#login(javax.jcr.Credentials, java.lang.String) + */ + public Session login(Credentials credentials, String workspaceName) + throws LoginException, NoSuchWorkspaceException, RepositoryException + { + // extract username and password + // TODO: determine support for general Credentials + String username = null; + char[] password = EMPTY_PASSWORD; + if (credentials != null && credentials instanceof SimpleCredentials) + { + username = ((SimpleCredentials)credentials).getUserID(); + password = ((SimpleCredentials)credentials).getPassword(); + } + + try + { + // construct the session + SessionImpl sessionImpl = new SessionImpl(this); + registerSession(sessionImpl); + + // authenticate user + AuthenticationService authenticationService = getServiceRegistry().getAuthenticationService(); + try + { + authenticationService.authenticate(username, password); + } + catch(AuthenticationException e) + { + deregisterSession(); + throw new LoginException("Alfresco Repository failed to authenticate credentials", e); + } + + // initialise the session + String ticket = authenticationService.getCurrentTicket(); + String sessionWorkspace = (workspaceName == null) ? defaultWorkspace : workspaceName; + sessionImpl.init(ticket, sessionWorkspace, getAttributes(credentials)); + + // session is now ready + Session session = sessionImpl.getProxy(); + return session; + } + catch(AlfrescoRuntimeException e) + { + deregisterSession(); + throw new RepositoryException(e); + } + } + + /* (non-Javadoc) + * @see javax.jcr.Repository#login(javax.jcr.Credentials) + */ + public Session login(Credentials credentials) + throws LoginException, RepositoryException + { + return login(credentials, null); + } + + /* (non-Javadoc) + * @see javax.jcr.Repository#login(java.lang.String) + */ + public Session login(String workspaceName) + throws LoginException, NoSuchWorkspaceException, RepositoryException + { + return login(null, workspaceName); + } + + /* (non-Javadoc) + * @see javax.jcr.Repository#login() + */ + public Session login() + throws LoginException, RepositoryException + { + return login(null, null); + } + + /** + * Get attributes from passed Credentials + * + * @param credentials the credentials to extract attribute from + * @return the attributes + */ + private Map getAttributes(Credentials credentials) + { + Map attributes = null; + if (credentials != null && credentials instanceof SimpleCredentials) + { + SimpleCredentials simpleCredentials = (SimpleCredentials)credentials; + String[] names = simpleCredentials.getAttributeNames(); + attributes = new HashMap(names.length); + for (String name : names) + { + attributes.put(name, simpleCredentials.getAttribute(name)); + } + } + return attributes; + } + + /** + * Register active session + * + * @param session + */ + private void registerSession(SessionImpl session) + throws RepositoryException + { + // only allow one active session + if (sessions.get() != null) + { + throw new RepositoryException("Only one active session is allowed per thread."); + } + + // record session in current thread + sessions.set(session); + } + + /** + * De-register current active session + */ + public void deregisterSession() + { + // remove session from current thread + sessions.set(null); + } + +} diff --git a/source/java/org/alfresco/jcr/repository/RepositoryImplTest.java b/source/java/org/alfresco/jcr/repository/RepositoryImplTest.java new file mode 100644 index 0000000000..e033ea62ed --- /dev/null +++ b/source/java/org/alfresco/jcr/repository/RepositoryImplTest.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.repository; + +import javax.jcr.LoginException; +import javax.jcr.NoSuchWorkspaceException; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; + +import org.alfresco.jcr.test.BaseJCRTest; + + +/** + * Test JCR Repository Implementation + * + * @author David Caruana + */ +public class RepositoryImplTest extends BaseJCRTest +{ + + public void testDescriptors() + { + String[] keys = repository.getDescriptorKeys(); + assertEquals(11, keys.length); + for (String key : keys) + { + String value = repository.getDescriptor(key); + assertNotNull(value); + } + + assertNotNull(repository.getDescriptor(Repository.REP_NAME_DESC)); + System.out.println(repository.getDescriptor(Repository.REP_NAME_DESC)); + assertNotNull(repository.getDescriptor(Repository.REP_VENDOR_DESC)); + assertNotNull(repository.getDescriptor(Repository.REP_VENDOR_URL_DESC)); + assertNotNull(repository.getDescriptor(Repository.REP_VERSION_DESC)); + System.out.println(repository.getDescriptor(Repository.REP_VERSION_DESC)); + assertNotNull(repository.getDescriptor(Repository.SPEC_NAME_DESC)); + assertNotNull(repository.getDescriptor(Repository.SPEC_VERSION_DESC)); + assertEquals("true", repository.getDescriptor(Repository.LEVEL_1_SUPPORTED)); + assertEquals("true", repository.getDescriptor(Repository.LEVEL_2_SUPPORTED)); + assertEquals("true", repository.getDescriptor(Repository.OPTION_TRANSACTIONS_SUPPORTED)); + assertEquals("true", repository.getDescriptor(Repository.QUERY_XPATH_DOC_ORDER)); + assertEquals("true", repository.getDescriptor(Repository.QUERY_XPATH_POS_INDEX)); + } + + public void testBadUsernameLogin() throws Exception + { + SimpleCredentials badUser = new SimpleCredentials("baduser", "".toCharArray()); + try + { + repository.login(badUser); + fail("Failed to catch bad username - username should not exist."); + } + catch (LoginException e) + { + } + } + + public void testBadPwdLogin() throws Exception + { + SimpleCredentials badPwd = new SimpleCredentials("superuser", "badpwd".toCharArray()); + try + { + repository.login(badPwd); + fail("Failed to catch bad password - password is invalid."); + } + catch (LoginException e) + { + } + } + + public void testNoCredentialsLogin() throws Exception + { + try + { + repository.login(); + fail("Failed to catch no credentials."); + } + catch (LoginException e) + { + } + } + + public void testLogin() + throws RepositoryException + { + SimpleCredentials good = new SimpleCredentials("superuser", "".toCharArray()); + try + { + Session session = repository.login(good, getWorkspace()); + assertNotNull(session); + session.logout(); + } + catch (LoginException e) + { + fail("Failed to login."); + } + + try + { + Session session = repository.login(good, null); + session.logout(); + } + catch (NoSuchWorkspaceException e) + { + fail("Failed to detect default workspace"); + } + } + +} + diff --git a/source/java/org/alfresco/jcr/session/SessionImpl.java b/source/java/org/alfresco/jcr/session/SessionImpl.java new file mode 100644 index 0000000000..b772e02d6a --- /dev/null +++ b/source/java/org/alfresco/jcr/session/SessionImpl.java @@ -0,0 +1,982 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.session; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.security.AccessControlException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.jcr.AccessDeniedException; +import javax.jcr.Credentials; +import javax.jcr.ImportUUIDBehavior; +import javax.jcr.InvalidItemStateException; +import javax.jcr.InvalidSerializedDataException; +import javax.jcr.Item; +import javax.jcr.ItemExistsException; +import javax.jcr.ItemNotFoundException; +import javax.jcr.LoginException; +import javax.jcr.NamespaceException; +import javax.jcr.NoSuchWorkspaceException; +import javax.jcr.Node; +import javax.jcr.PathNotFoundException; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.UnsupportedRepositoryOperationException; +import javax.jcr.ValueFactory; +import javax.jcr.Workspace; +import javax.jcr.lock.LockException; +import javax.jcr.nodetype.ConstraintViolationException; +import javax.jcr.nodetype.NoSuchNodeTypeException; +import javax.jcr.version.VersionException; +import javax.transaction.HeuristicMixedException; +import javax.transaction.HeuristicRollbackException; +import javax.transaction.NotSupportedException; +import javax.transaction.RollbackException; +import javax.transaction.SystemException; +import javax.transaction.UserTransaction; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.jcr.dictionary.JCRNamespacePrefixResolver; +import org.alfresco.jcr.dictionary.NamespaceRegistryImpl; +import org.alfresco.jcr.dictionary.NodeTypeManagerImpl; +import org.alfresco.jcr.exporter.JCRDocumentXMLExporter; +import org.alfresco.jcr.exporter.JCRSystemXMLExporter; +import org.alfresco.jcr.importer.JCRImportHandler; +import org.alfresco.jcr.item.ItemImpl; +import org.alfresco.jcr.item.ItemResolver; +import org.alfresco.jcr.item.JCRPath; +import org.alfresco.jcr.item.JCRTypeConverter; +import org.alfresco.jcr.item.NodeImpl; +import org.alfresco.jcr.item.ValueFactoryImpl; +import org.alfresco.jcr.repository.RepositoryImpl; +import org.alfresco.jcr.util.JCRProxyFactory; +import org.alfresco.repo.importer.ImporterComponent; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.service.cmr.lock.LockService; +import org.alfresco.service.cmr.lock.LockType; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.view.ExporterCrawlerParameters; +import org.alfresco.service.cmr.view.ExporterService; +import org.alfresco.service.cmr.view.ImporterBinding; +import org.alfresco.service.cmr.view.ImporterException; +import org.alfresco.service.cmr.view.Location; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; +import org.dom4j.io.OutputFormat; +import org.dom4j.io.XMLWriter; +import org.xml.sax.ContentHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLReaderFactory; + + +/** + * Alfresco Implementation of a JCR Session + * + * @author David Caruana + */ +public class SessionImpl implements Session +{ + /** Parent Repository */ + private RepositoryImpl repository; + + /** Transaction Id */ + private String trxId; + + /** Session Attributes */ + private Map attributes; + + /** Workspace Store Reference */ + private StoreRef workspaceStore; + + /** Workspace */ + private WorkspaceImpl workspace = null; + + /** Type Converter */ + private JCRTypeConverter typeConverter = null; + + /** Session based Namespace Resolver */ + private NamespaceRegistryImpl namespaceResolver; + + /** Type Manager */ + private NodeTypeManagerImpl typeManager = null; + + /** Value Factory */ + private ValueFactoryImpl valueFactory = null; + + /** Session Proxy */ + private Session proxy = null; + + /** Session Isolation Strategy */ + private SessionIsolation sessionIsolation = null; + + /** Authenticated ticket */ + private String ticket = null; + + + /** + * Construct + * + * @param repository parent repository + * @throws NoSuchWorkspaceException + */ + public SessionImpl(RepositoryImpl repository) + { + // intialise session + this.repository = repository; + this.typeConverter = new JCRTypeConverter(this); + this.namespaceResolver = new NamespaceRegistryImpl(true, new JCRNamespacePrefixResolver(repository.getServiceRegistry().getNamespaceService())); + this.typeManager = new NodeTypeManagerImpl(this, namespaceResolver.getNamespaceService()); + this.valueFactory = new ValueFactoryImpl(this); + } + + /** + * Initialise Session + * + * @param ticket authentication ticket + * @param workspaceName workspace name + * @param attributes session attributes + * @throws RepositoryException + */ + public void init(String ticket, String workspaceName, Map attributes) + throws RepositoryException + { + // create appropriate strategy for handling session isolation + // TODO: Support full session isolation as described in the JCR specification + String trxId = AlfrescoTransactionSupport.getTransactionId(); + sessionIsolation = (trxId == null) ? new InnerTransaction() : new OuterTransaction(); + sessionIsolation.begin(); + + // initialise the session + this.ticket = ticket; + this.attributes = (attributes == null) ? new HashMap() : attributes; + this.workspaceStore = getWorkspaceStore(workspaceName); + } + + /** + * Create proxied Session + * + * @return JCR Session + */ + public Session getProxy() + { + if (proxy == null) + { + proxy = (Session)JCRProxyFactory.create(this, Session.class, this); + } + return proxy; + } + + /** + * Get the Repository Impl + * + * @return repository impl + */ + public RepositoryImpl getRepositoryImpl() + { + return repository; + } + + /** + * Get the session Ticket + * + * @return ticket + */ + public String getTicket() + { + return ticket; + } + + /** + * Get the associated transaction Id + * + * @return transaction id + */ + public String getTransactionId() + { + return trxId; + } + + /** + * Get the Type Converter + * + * @return the type converter + */ + public JCRTypeConverter getTypeConverter() + { + return typeConverter; + } + + /** + * Get the Type Manager + * + * @return the type manager + */ + public NodeTypeManagerImpl getTypeManager() + { + return typeManager; + } + + /** + * Get the Namespace Resolver + * + * @return the session based Namespace Resolver + */ + public NamespacePrefixResolver getNamespaceResolver() + { + return namespaceResolver.getNamespaceService(); + } + + /** + * Get the Workspace Store + * + * @return the workspace store reference + */ + public StoreRef getWorkspaceStore() + { + return workspaceStore; + } + + + // + // JCR Session + // + + /* (non-Javadoc) + * @see javax.jcr.Session#getRepository() + */ + public Repository getRepository() + { + return repository; + } + + /* (non-Javadoc) + * @see javax.jcr.Session#getUserID() + */ + public String getUserID() + { + return getRepositoryImpl().getServiceRegistry().getAuthenticationService().getCurrentUserName(); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#getAttribute(java.lang.String) + */ + public Object getAttribute(String name) + { + return attributes.get(name); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#getAttributeNames() + */ + public String[] getAttributeNames() + { + String[] names = (String[]) attributes.keySet().toArray(new String[attributes.keySet().size()]); + return names; + } + + /* (non-Javadoc) + * @see javax.jcr.Session#getWorkspace() + */ + public Workspace getWorkspace() + { + if (workspace == null) + { + workspace = new WorkspaceImpl(this); + } + return workspace.getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#impersonate(javax.jcr.Credentials) + */ + public Session impersonate(Credentials credentials) throws LoginException, RepositoryException + { + // TODO: Implement when impersonation permission added to Alfresco Repository + throw new LoginException("Insufficient permission to impersonate"); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#getRootNode() + */ + public Node getRootNode() throws RepositoryException + { + NodeRef nodeRef = getRepositoryImpl().getServiceRegistry().getNodeService().getRootNode(workspaceStore); + NodeImpl nodeImpl = new NodeImpl(this, nodeRef); + return nodeImpl.getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#getNodeByUUID(java.lang.String) + */ + public Node getNodeByUUID(String uuid) throws ItemNotFoundException, RepositoryException + { + NodeRef nodeRef = new NodeRef(workspaceStore, uuid); + boolean exists = getRepositoryImpl().getServiceRegistry().getNodeService().exists(nodeRef); + if (exists == false) + { + throw new ItemNotFoundException(); + } + NodeImpl nodeImpl = new NodeImpl(this, nodeRef); + return nodeImpl.getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#getItem(java.lang.String) + */ + public Item getItem(String absPath) throws PathNotFoundException, RepositoryException + { + NodeRef nodeRef = getRepositoryImpl().getServiceRegistry().getNodeService().getRootNode(workspaceStore); + ItemImpl itemImpl = ItemResolver.findItem(this, nodeRef, absPath); + return itemImpl.getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#itemExists(java.lang.String) + */ + public boolean itemExists(String absPath) throws RepositoryException + { + ParameterCheck.mandatoryString("absPath", absPath); + NodeRef nodeRef = getRepositoryImpl().getServiceRegistry().getNodeService().getRootNode(workspaceStore); + return ItemResolver.itemExists(this, nodeRef, absPath); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#move(java.lang.String, java.lang.String) + */ + public void move(String srcAbsPath, String destAbsPath) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, RepositoryException + { + ParameterCheck.mandatoryString("srcAbsPath", srcAbsPath); + ParameterCheck.mandatoryString("destAbsPath", destAbsPath); + + // Find source node + NodeService nodeService = getRepositoryImpl().getServiceRegistry().getNodeService(); + NodeRef rootRef = nodeService.getRootNode(workspaceStore); + NodeRef sourceRef = ItemResolver.getNodeRef(this, rootRef, srcAbsPath); + if (sourceRef == null) + { + throw new PathNotFoundException("Source path " + srcAbsPath + " cannot be found."); + } + + // Find dest node + NodeRef destRef = null; + QName destName = null; + Path destPath = new JCRPath(getNamespaceResolver(), destAbsPath).getPath(); + if (destPath.size() == 1) + { + destRef = rootRef; + destName = ((JCRPath.SimpleElement)destPath.get(0)).getQName(); + } + else + { + Path destParentPath = destPath.subPath(destPath.size() -2); + destRef = ItemResolver.getNodeRef(this, rootRef, destParentPath.toPrefixString(getNamespaceResolver())); + if (destRef == null) + { + throw new PathNotFoundException("Destination path " + destParentPath + " cannot be found."); + } + destName = ((JCRPath.SimpleElement)destPath.get(destPath.size() -1)).getQName(); + } + + // Validate name + // TODO: Replace with proper name validation + if (destName.getLocalName().indexOf('[') != -1 || destName.getLocalName().indexOf(']') != -1) + { + throw new RepositoryException("Node name '" + destName + "' is invalid"); + } + + // Determine child association type for destination + ChildAssociationRef childAssocRef = nodeService.getPrimaryParent(sourceRef); + + // Move node + nodeService.moveNode(sourceRef, destRef, childAssocRef.getTypeQName(), destName); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#save() + */ + public void save() throws AccessDeniedException, ItemExistsException, ConstraintViolationException, InvalidItemStateException, VersionException, LockException, NoSuchNodeTypeException, RepositoryException + { + sessionIsolation.commit(); + // Note: start a new transaction for subsequent session changes + sessionIsolation.begin(); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#refresh(boolean) + */ + public void refresh(boolean keepChanges) throws RepositoryException + { + if (keepChanges) + { + throw new UnsupportedRepositoryOperationException("Keep changes is not supported."); + } + sessionIsolation.rollback(); + // Note: start a new transaction for subsequent session changes + sessionIsolation.begin(); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#hasPendingChanges() + */ + public boolean hasPendingChanges() throws RepositoryException + { + return AlfrescoTransactionSupport.isDirty(); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#getValueFactory() + */ + public ValueFactory getValueFactory() throws UnsupportedRepositoryOperationException, RepositoryException + { + return valueFactory.getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#checkPermission(java.lang.String, java.lang.String) + */ + public void checkPermission(String absPath, String actions) throws AccessControlException, RepositoryException + { + // locate noderef for path + NodeService nodeService = getRepositoryImpl().getServiceRegistry().getNodeService(); + NodeRef rootRef = nodeService.getRootNode(getWorkspaceStore()); + NodeRef nodeRef = ItemResolver.getNodeRef(this, rootRef, absPath); + if (nodeRef == null) + { + throw new AccessControlException("Unable to determine access control for path " + absPath); + } + + // test each of the actions specified + PermissionService permissionService = getRepositoryImpl().getServiceRegistry().getPermissionService(); + String[] checkActions = actions.split(","); + for (String checkAction : checkActions) + { + checkAction = checkAction.trim(); + AccessStatus accessStatus = null; + if (checkAction.equals("add_node")) + { + accessStatus = permissionService.hasPermission(nodeRef, PermissionService.ADD_CHILDREN); + } + else if (checkAction.equals("set_property")) + { + accessStatus = permissionService.hasPermission(nodeRef, PermissionService.WRITE_PROPERTIES); + } + else if (checkAction.equals("remove")) + { + accessStatus = permissionService.hasPermission(nodeRef, PermissionService.DELETE); + } + else if (checkAction.equals("read")) + { + accessStatus = permissionService.hasPermission(nodeRef, PermissionService.READ); + } + else + { + // fall-through check for alfresco specific permissions + accessStatus = permissionService.hasPermission(nodeRef, checkAction); + } + + // abort if permission not granted + if (accessStatus == AccessStatus.DENIED) + { + throw new AccessControlException("Permission " + checkAction + " not granted on path " + absPath); + } + } + } + + /* (non-Javadoc) + * @see javax.jcr.Session#getImportContentHandler(java.lang.String, int) + */ + public ContentHandler getImportContentHandler(String parentAbsPath, int uuidBehavior) throws PathNotFoundException, ConstraintViolationException, VersionException, LockException, RepositoryException + { + // locate noderef for path + NodeService nodeService = getRepositoryImpl().getServiceRegistry().getNodeService(); + NodeRef rootRef = nodeService.getRootNode(getWorkspaceStore()); + NodeRef nodeRef = ItemResolver.getNodeRef(this, rootRef, parentAbsPath); + if (nodeRef == null) + { + throw new PathNotFoundException("Parent path " + parentAbsPath + " does not exist."); + } + + // create content handler for import + JCRImportHandler jcrImportHandler = new JCRImportHandler(this); + ImporterComponent importerComponent = getRepositoryImpl().getImporterComponent(); + return importerComponent.handlerImport(nodeRef, null, jcrImportHandler, new JCRImportBinding(uuidBehavior), null); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#importXML(java.lang.String, java.io.InputStream, int) + */ + public void importXML(String parentAbsPath, InputStream in, int uuidBehavior) throws IOException, PathNotFoundException, ItemExistsException, ConstraintViolationException, VersionException, InvalidSerializedDataException, LockException, RepositoryException + { + ContentHandler handler = getImportContentHandler(parentAbsPath, uuidBehavior); + + try + { + XMLReader parser = XMLReaderFactory.createXMLReader(); + parser.setContentHandler(handler); + parser.setFeature("http://xml.org/sax/features/namespaces", true); + parser.setFeature("http://xml.org/sax/features/namespace-prefixes", false); + parser.parse(new InputSource(in)); + } + catch (SAXException se) + { + // check for wrapped repository exception + Exception e = se.getException(); + if (e != null && e instanceof AlfrescoRuntimeException) + { + throw (AlfrescoRuntimeException) e; + } + else + { + throw new InvalidSerializedDataException("Failed to import provided xml stream", se); + } + } + } + + /* (non-Javadoc) + * @see javax.jcr.Session#exportSystemView(java.lang.String, org.xml.sax.ContentHandler, boolean, boolean) + */ + public void exportSystemView(String absPath, ContentHandler contentHandler, boolean skipBinary, boolean noRecurse) throws PathNotFoundException, SAXException, RepositoryException + { + JCRSystemXMLExporter exporter = new JCRSystemXMLExporter(this, contentHandler); + ExporterCrawlerParameters parameters = createExportParameters(absPath, skipBinary, noRecurse); + ExporterService exporterService = getRepositoryImpl().getServiceRegistry().getExporterService(); + exporterService.exportView(exporter, parameters, null); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#exportSystemView(java.lang.String, java.io.OutputStream, boolean, boolean) + */ + public void exportSystemView(String absPath, OutputStream out, boolean skipBinary, boolean noRecurse) throws IOException, PathNotFoundException, RepositoryException + { + JCRSystemXMLExporter exporter = new JCRSystemXMLExporter(this, createExportContentHandler(out)); + ExporterCrawlerParameters parameters = createExportParameters(absPath, skipBinary, noRecurse); + ExporterService exporterService = getRepositoryImpl().getServiceRegistry().getExporterService(); + exporterService.exportView(exporter, parameters, null); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#exportDocumentView(java.lang.String, org.xml.sax.ContentHandler, boolean, boolean) + */ + public void exportDocumentView(String absPath, ContentHandler contentHandler, boolean skipBinary, boolean noRecurse) throws PathNotFoundException, SAXException, RepositoryException + { + JCRDocumentXMLExporter exporter = new JCRDocumentXMLExporter(this, contentHandler); + ExporterCrawlerParameters parameters = createExportParameters(absPath, skipBinary, noRecurse); + ExporterService exporterService = getRepositoryImpl().getServiceRegistry().getExporterService(); + exporterService.exportView(exporter, parameters, null); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#exportDocumentView(java.lang.String, java.io.OutputStream, boolean, boolean) + */ + public void exportDocumentView(String absPath, OutputStream out, boolean skipBinary, boolean noRecurse) throws IOException, PathNotFoundException, RepositoryException + { + JCRDocumentXMLExporter exporter = new JCRDocumentXMLExporter(this, createExportContentHandler(out)); + ExporterCrawlerParameters parameters = createExportParameters(absPath, skipBinary, noRecurse); + ExporterService exporterService = getRepositoryImpl().getServiceRegistry().getExporterService(); + exporterService.exportView(exporter, parameters, null); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#setNamespacePrefix(java.lang.String, java.lang.String) + */ + public void setNamespacePrefix(String prefix, String uri) throws NamespaceException, RepositoryException + { + namespaceResolver.registerNamespace(prefix, uri); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#getNamespacePrefixes() + */ + public String[] getNamespacePrefixes() throws RepositoryException + { + return namespaceResolver.getPrefixes(); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#getNamespaceURI(java.lang.String) + */ + public String getNamespaceURI(String prefix) throws NamespaceException, RepositoryException + { + return namespaceResolver.getURI(prefix); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#getNamespacePrefix(java.lang.String) + */ + public String getNamespacePrefix(String uri) throws NamespaceException, RepositoryException + { + return namespaceResolver.getPrefix(uri); + } + + /* (non-Javadoc) + * @see javax.jcr.Session#logout() + */ + public void logout() + { + if (isLive()) + { + // invalidate authentication + getRepositoryImpl().getServiceRegistry().getAuthenticationService().invalidateTicket(getTicket()); + ticket = null; + + // clean up resources + try + { + sessionIsolation.rollback(); + } + catch(RepositoryException e) + { + // force logout + } + repository.deregisterSession(); + } + } + + /* (non-Javadoc) + * @see javax.jcr.Session#isLive() + */ + public boolean isLive() + { + return ticket != null; + } + + /* (non-Javadoc) + * @see javax.jcr.Session#addLockToken(java.lang.String) + */ + public void addLockToken(String lt) + { + // TODO: UnsupportedRepositoryOperationException + } + + /* (non-Javadoc) + * @see javax.jcr.Session#getLockTokens() + */ + public String[] getLockTokens() + { + LockService lockService = getRepositoryImpl().getServiceRegistry().getLockService(); + List nodeRefs = lockService.getLocks(getWorkspaceStore(), LockType.WRITE_LOCK); + String[] tokens = new String[nodeRefs.size()]; + int i = 0; + for (NodeRef nodeRef : nodeRefs) + { + tokens[i++] = nodeRef.toString(); + } + return tokens; + } + + /* (non-Javadoc) + * @see javax.jcr.Session#removeLockToken(java.lang.String) + */ + public void removeLockToken(String lt) + { + // TODO: UnsupportedRepositoryOperationException + } + + /** + * Gets the workspace store reference for the given workspace name + * + * @param workspaceName the workspace name + * @return the store reference + * @throws NoSuchWorkspaceException + */ + private StoreRef getWorkspaceStore(String workspaceName) + throws NoSuchWorkspaceException + { + if (workspaceName == null) + { + // TODO: Provide a default "Null Workspace" as per JCR specification + throw new NoSuchWorkspaceException("A default workspace could not be established."); + } + + StoreRef workspace = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, workspaceName); + NodeService nodeService = getRepositoryImpl().getServiceRegistry().getNodeService(); + boolean exists = false; + try + { + exists = nodeService.exists(workspace); + } + catch(org.alfresco.repo.security.permissions.AccessDeniedException e) + { + // note: fallthrough - store does not exist + } + + if (!exists) + { + throw new NoSuchWorkspaceException("Workspace " + workspaceName + " does not exist."); + } + return workspace; + } + + /** + * Create a Content Handler that outputs to the specified output stream. + * + * @param output stream the output stream to write to + * @return the content handler + */ + private ContentHandler createExportContentHandler(OutputStream output) + throws RepositoryException + { + // Define output format + OutputFormat format = OutputFormat.createPrettyPrint(); + format.setNewLineAfterDeclaration(false); + format.setIndentSize(2); + format.setEncoding("UTF-8"); + + // Construct an XML Writer + try + { + return new XMLWriter(output, format); + } + catch (UnsupportedEncodingException e) + { + throw new RepositoryException("Failed to create content handler for export", e); + } + } + + /** + * Create Export Parameters + * + * @param exportPath path to export from + * @param skipBinary skip binary content in export + * @param noRecurse do not recurse to children + * @return export parameters + */ + private ExporterCrawlerParameters createExportParameters(String exportPath, boolean skipBinary, boolean noRecurse) + { + // construct exporter parameters + ExporterCrawlerParameters parameters = new ExporterCrawlerParameters(); + Location exportFrom = new Location(getWorkspaceStore()); + exportFrom.setPath(exportPath); + parameters.setExportFrom(exportFrom); + parameters.setCrawlSelf(true); + parameters.setCrawlContent(!skipBinary); + parameters.setCrawlChildNodes(!noRecurse); + parameters.setCrawlNullProperties(false); + return parameters; + } + + /** + * JCR Session Import Binding + */ + private class JCRImportBinding implements ImporterBinding + { + private ImporterBinding.UUID_BINDING uuidBinding; + + /** + * Construct + * + * @param uuidBehaviour JCR Import UUID Behaviour + */ + private JCRImportBinding(int uuidBehaviour) + { + switch (uuidBehaviour) + { + case ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW: + uuidBinding = ImporterBinding.UUID_BINDING.CREATE_NEW; + break; + case ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING: + uuidBinding = ImporterBinding.UUID_BINDING.REMOVE_EXISTING; + break; + case ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING: + uuidBinding = ImporterBinding.UUID_BINDING.REPLACE_EXISTING; + break; + case ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW: + uuidBinding = ImporterBinding.UUID_BINDING.THROW_ON_COLLISION; + break; + default: + throw new ImporterException("Unknown Import UUID Behaviour: " + uuidBehaviour); + } + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.cmr.view.ImporterBinding#getUUIDBinding() + */ + public UUID_BINDING getUUIDBinding() + { + return uuidBinding; + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.cmr.view.ImporterBinding#getValue(java.lang.String) + */ + public String getValue(String key) + { + return null; + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.cmr.view.ImporterBinding#searchWithinTransaction() + */ + public boolean allowReferenceWithinTransaction() + { + return false; + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.cmr.view.ImporterBinding#getExcludedClasses() + */ + public QName[] getExcludedClasses() + { + return null; + } + } + + // + // Session / Transaction Management + // + + /** + * Strategy for handling Session Isolation + */ + private interface SessionIsolation + { + // Start transaction + public void begin() throws RepositoryException; + + // Commit transaction + public void commit() throws RepositoryException; + + // Rollback transaction + public void rollback() throws RepositoryException; + } + + + /** + * Implementation of session isolation which relies on an outer transaction + * to control the isolation boundary. + * + * @author davidc + */ + private class OuterTransaction implements SessionIsolation + { + /* (non-Javadoc) + * @see org.alfresco.jcr.session.SessionImpl.SessionIsolation#begin() + */ + public void begin() throws RepositoryException + { + } + + /* (non-Javadoc) + * @see org.alfresco.jcr.session.SessionImpl.SessionIsolation#commit() + */ + public void commit() throws RepositoryException + { + } + + /* (non-Javadoc) + * @see org.alfresco.jcr.session.SessionImpl.SessionIsolation#rollback() + */ + public void rollback() throws RepositoryException + { + } + } + + /** + * Session isolation strategy which uses transactions to control the isolation. + * + * @author davidc + */ + private class InnerTransaction implements SessionIsolation + { + private UserTransaction userTransaction = null; + + /* (non-Javadoc) + * @see org.alfresco.jcr.session.SessionImpl.SessionIsolation#begin() + */ + public void begin() + throws RepositoryException + { + try + { + UserTransaction trx = repository.getServiceRegistry().getTransactionService().getUserTransaction(); + trx.begin(); + userTransaction = trx; + } + catch (NotSupportedException e) + { + throw new RepositoryException("Failed to start Repository transaction", e); + } + catch (SystemException e) + { + throw new RepositoryException("Failed to start Repository transaction", e); + } + } + + /* (non-Javadoc) + * @see org.alfresco.jcr.session.SessionImpl.SessionIsolation#commit() + */ + public void commit() + throws RepositoryException + { + try + { + userTransaction.commit(); + } + catch (HeuristicRollbackException e) + { + throw new RepositoryException("Failed to commit Repository transaction", e); + } + catch (HeuristicMixedException e) + { + throw new RepositoryException("Failed to commit Repository transaction", e); + } + catch (RollbackException e) + { + throw new RepositoryException("Failed to commit Repository transaction", e); + } + catch (SystemException e) + { + throw new RepositoryException("Failed to commit Repository transaction", e); + } + } + + /* (non-Javadoc) + * @see org.alfresco.jcr.session.SessionImpl.SessionIsolation#rollback() + */ + public void rollback() + throws RepositoryException + { + try + { + userTransaction.rollback(); + } + catch (SystemException e) + { + throw new RepositoryException("Failed to rollback Repository transaction", e); + } + } + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/jcr/session/SessionImplTest.java b/source/java/org/alfresco/jcr/session/SessionImplTest.java new file mode 100644 index 0000000000..749bf13f3f --- /dev/null +++ b/source/java/org/alfresco/jcr/session/SessionImplTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.session; + +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; + +import org.alfresco.jcr.test.BaseJCRTest; + + +/** + * Test JCR Session + * + * @author David Caruana + */ +public class SessionImplTest extends BaseJCRTest +{ + protected Session superuserSession; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + SimpleCredentials superuser = new SimpleCredentials("superuser", "".toCharArray()); + superuser.setAttribute("attr1", "superuserValue"); + superuser.setAttribute("attr2", new Integer(1)); + superuserSession = repository.login(superuser, getWorkspace()); + } + + @Override + protected void tearDown() throws Exception + { + superuserSession.logout(); + super.tearDown(); + } + + public void testRepository() + throws RepositoryException + { + Repository sessionRepository = superuserSession.getRepository(); + assertNotNull(sessionRepository); + assertEquals(repository, sessionRepository); + } + + public void testUserId() + { + { + String userId = superuserSession.getUserID(); + assertNotNull(userId); + assertEquals("superuser", userId); + } + } + + public void testAttributes() + { + { + String[] names = superuserSession.getAttributeNames(); + assertNotNull(names); + assertEquals(2, names.length); + String value1 = (String)superuserSession.getAttribute("attr1"); + assertNotNull(value1); + assertEquals("superuserValue", value1); + Integer value2 = (Integer)superuserSession.getAttribute("attr2"); + assertNotNull(value2); + assertEquals(new Integer(1), value2); + String value3 = (String)superuserSession.getAttribute("unknown"); + assertNull(value3); + } + } + + public void testLogout() + { + boolean isLive = superuserSession.isLive(); + assertTrue(isLive); + superuserSession.logout(); + isLive = superuserSession.isLive(); + assertFalse(isLive); + } + +} + diff --git a/source/java/org/alfresco/jcr/session/WorkspaceImpl.java b/source/java/org/alfresco/jcr/session/WorkspaceImpl.java new file mode 100644 index 0000000000..037c9bba17 --- /dev/null +++ b/source/java/org/alfresco/jcr/session/WorkspaceImpl.java @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.session; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.AccessDeniedException; +import javax.jcr.InvalidItemStateException; +import javax.jcr.InvalidSerializedDataException; +import javax.jcr.ItemExistsException; +import javax.jcr.NamespaceRegistry; +import javax.jcr.NoSuchWorkspaceException; +import javax.jcr.PathNotFoundException; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.UnsupportedRepositoryOperationException; +import javax.jcr.Workspace; +import javax.jcr.lock.LockException; +import javax.jcr.nodetype.ConstraintViolationException; +import javax.jcr.nodetype.NodeTypeManager; +import javax.jcr.observation.ObservationManager; +import javax.jcr.query.QueryManager; +import javax.jcr.version.Version; +import javax.jcr.version.VersionException; + +import org.alfresco.jcr.item.ItemResolver; +import org.alfresco.jcr.item.JCRPath; +import org.alfresco.jcr.query.QueryManagerImpl; +import org.alfresco.jcr.util.JCRProxyFactory; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.CopyService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; +import org.xml.sax.ContentHandler; + +/** + * Alfresco implementation of a JCR Workspace + * + * @author David Caruana + */ +public class WorkspaceImpl implements Workspace +{ + + private SessionImpl session; + private Workspace proxy = null; + private QueryManagerImpl queryManager = null; + + /** + * Construct + * + * @param session the session + */ + public WorkspaceImpl(SessionImpl session) + { + this.session = session; + } + + /** + * Get proxied JCR Workspace + * + * @return proxied JCR Workspace + */ + public Workspace getProxy() + { + if (proxy == null) + { + proxy = (Workspace)JCRProxyFactory.create(this, Workspace.class, session); + } + return proxy; + } + + + /* (non-Javadoc) + * @see javax.jcr.Workspace#getSession() + */ + public Session getSession() + { + return session.getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.Workspace#getName() + */ + public String getName() + { + return session.getWorkspaceStore().getIdentifier(); + } + + /* (non-Javadoc) + * @see javax.jcr.Workspace#copy(java.lang.String, java.lang.String) + */ + public void copy(String srcAbsPath, String destAbsPath) throws ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, RepositoryException + { + ParameterCheck.mandatoryString("srcAbsPath", srcAbsPath); + ParameterCheck.mandatoryString("destAbsPath", destAbsPath); + + // find source node + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + NodeRef rootRef = nodeService.getRootNode(session.getWorkspaceStore()); + NodeRef sourceRef = ItemResolver.getNodeRef(session, rootRef, srcAbsPath); + if (sourceRef == null) + { + throw new PathNotFoundException("Source path " + srcAbsPath + " cannot be found."); + } + + // find dest node + NodeRef destRef = null; + QName destName = null; + Path destPath = new JCRPath(session.getNamespaceResolver(), destAbsPath).getPath(); + if (destPath.size() == 1) + { + destRef = rootRef; + destName = ((JCRPath.SimpleElement)destPath.get(0)).getQName(); + } + else + { + Path destParentPath = destPath.subPath(destPath.size() -2); + destRef = ItemResolver.getNodeRef(session, rootRef, destParentPath.toPrefixString(session.getNamespaceResolver())); + if (destRef == null) + { + throw new PathNotFoundException("Destination path " + destParentPath + " cannot be found."); + } + destName = ((JCRPath.SimpleElement)destPath.get(destPath.size() -1)).getQName(); + } + + // validate name + // TODO: Replace with proper name validation + if (destName.getLocalName().indexOf('[') != -1 || destName.getLocalName().indexOf(']') != -1) + { + throw new RepositoryException("Node name '" + destName + "' is invalid"); + } + + // determine child association type for destination + ChildAssociationRef childAssocRef = nodeService.getPrimaryParent(sourceRef); + + // copy node + CopyService copyService = session.getRepositoryImpl().getServiceRegistry().getCopyService(); + copyService.copy(sourceRef, destRef, childAssocRef.getTypeQName(), destName); + + // finally save + session.save(); + } + + /* (non-Javadoc) + * @see javax.jcr.Workspace#copy(java.lang.String, java.lang.String, java.lang.String) + */ + public void copy(String srcWorkspace, String srcAbsPath, String destAbsPath) throws NoSuchWorkspaceException, ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + /* (non-Javadoc) + * @see javax.jcr.Workspace#clone(java.lang.String, java.lang.String, java.lang.String, boolean) + */ + public void clone(String srcWorkspace, String srcAbsPath, String destAbsPath, boolean removeExisting) throws NoSuchWorkspaceException, ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + /* (non-Javadoc) + * @see javax.jcr.Workspace#move(java.lang.String, java.lang.String) + */ + public void move(String srcAbsPath, String destAbsPath) throws ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, RepositoryException + { + session.move(srcAbsPath, destAbsPath); + session.save(); + } + + /* (non-Javadoc) + * @see javax.jcr.Workspace#restore(javax.jcr.version.Version[], boolean) + */ + public void restore(Version[] versions, boolean removeExisting) throws ItemExistsException, UnsupportedRepositoryOperationException, VersionException, LockException, InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + /* (non-Javadoc) + * @see javax.jcr.Workspace#getQueryManager() + */ + public QueryManager getQueryManager() throws RepositoryException + { + if (queryManager == null) + { + queryManager = new QueryManagerImpl(session); + } + return queryManager; + } + + /* (non-Javadoc) + * @see javax.jcr.Workspace#getNamespaceRegistry() + */ + public NamespaceRegistry getNamespaceRegistry() throws RepositoryException + { + return session.getRepositoryImpl().getNamespaceRegistry(); + } + + /* (non-Javadoc) + * @see javax.jcr.Workspace#getNodeTypeManager() + */ + public NodeTypeManager getNodeTypeManager() throws RepositoryException + { + return session.getTypeManager(); + } + + /* (non-Javadoc) + * @see javax.jcr.Workspace#getObservationManager() + */ + public ObservationManager getObservationManager() throws UnsupportedRepositoryOperationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + /* (non-Javadoc) + * @see javax.jcr.Workspace#getAccessibleWorkspaceNames() + */ + public String[] getAccessibleWorkspaceNames() throws RepositoryException + { + NodeService nodeService = session.getRepositoryImpl().getServiceRegistry().getNodeService(); + List storeRefs = nodeService.getStores(); + List workspaceStores = new ArrayList(); + for (StoreRef storeRef : storeRefs) + { + if (storeRef.getProtocol().equals(StoreRef.PROTOCOL_WORKSPACE)) + { + workspaceStores.add(storeRef.getIdentifier()); + } + } + return workspaceStores.toArray(new String[workspaceStores.size()]); + } + + /* (non-Javadoc) + * @see javax.jcr.Workspace#getImportContentHandler(java.lang.String, int) + */ + public ContentHandler getImportContentHandler(String parentAbsPath, int uuidBehavior) throws PathNotFoundException, ConstraintViolationException, VersionException, LockException, AccessDeniedException, RepositoryException + { + return session.getImportContentHandler(parentAbsPath, uuidBehavior); + } + + /* (non-Javadoc) + * @see javax.jcr.Workspace#importXML(java.lang.String, java.io.InputStream, int) + */ + public void importXML(String parentAbsPath, InputStream in, int uuidBehavior) throws IOException, PathNotFoundException, ItemExistsException, ConstraintViolationException, InvalidSerializedDataException, LockException, AccessDeniedException, RepositoryException + { + session.importXML(parentAbsPath, in, uuidBehavior); + } + +} diff --git a/source/java/org/alfresco/jcr/tck/RepositoryStartupServlet.java b/source/java/org/alfresco/jcr/tck/RepositoryStartupServlet.java new file mode 100644 index 0000000000..88b6dd94c6 --- /dev/null +++ b/source/java/org/alfresco/jcr/tck/RepositoryStartupServlet.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.tck; + +import java.util.Hashtable; + +import javax.jcr.Repository; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; + +import org.alfresco.jcr.repository.RepositoryFactory; +import org.alfresco.jcr.repository.RepositoryImpl; +import org.alfresco.jcr.test.TestData; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + + +/** + * Setup Repository for access via JNDI by TCK Web Application + * + * @author David Caruana + */ +public class RepositoryStartupServlet extends HttpServlet +{ + private static final long serialVersionUID = -4763518135895358778L; + + private static InitialContext jndiContext; + + private final static String repositoryName = "Alfresco.Repository"; + + + /** + * Initializes the servlet + * + * @throws ServletException + */ + public void init() + throws ServletException + { + super.init(); + + WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); + RepositoryImpl repository = (RepositoryImpl)context.getBean(RepositoryFactory.REPOSITORY_BEAN); + repository.setDefaultWorkspace(TestData.TEST_WORKSPACE); + + try + { + Hashtable env = new Hashtable(); + env.put(Context.PROVIDER_URL, "http://www.alfresco.org"); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.day.crx.jndi.provider.MemoryInitialContextFactory"); + jndiContext = new InitialContext(env); + jndiContext.bind(repositoryName, (Repository)repository); + } + catch (NamingException e) + { + throw new ServletException(e); + } + } + + /** + * Destroy the servlet + */ + public void destroy() + { + super.destroy(); + + if (jndiContext != null) + { + try + { + jndiContext.unbind(repositoryName); + } + catch (NamingException e) + { + // Note: Itentionally ignore... + } + } + } + + +} diff --git a/source/java/org/alfresco/jcr/tck/testExcludeList.txt b/source/java/org/alfresco/jcr/tck/testExcludeList.txt new file mode 100644 index 0000000000..5d1efcc81c --- /dev/null +++ b/source/java/org/alfresco/jcr/tck/testExcludeList.txt @@ -0,0 +1,2 @@ +version=0.6.0 +list=org.apache.jackrabbit.test.api.AddNodeTest,org.apache.jackrabbit.test.api.CheckPermissionTest,org.apache.jackrabbit.test.api.DocumentViewImportTest,org.apache.jackrabbit.test.api.ImpersonateTest,org.apache.jackrabbit.test.api.lock.SetValueLockExceptionTest,org.apache.jackrabbit.test.api.NamespaceRegistryTest,org.apache.jackrabbit.test.api.NodeAddMixinTest,org.apache.jackrabbit.test.api.NodeCanAddMixinTest,org.apache.jackrabbit.test.api.NodeItemIsModifiedTest,org.apache.jackrabbit.test.api.NodeItemIsNewTest,org.apache.jackrabbit.test.api.NodeOrderableChildNodesTest,org.apache.jackrabbit.test.api.NodeRemoveMixinTest,org.apache.jackrabbit.test.api.NodeTest,org.apache.jackrabbit.test.api.NodeUUIDTest,org.apache.jackrabbit.test.api.PropertyItemIsModifiedTest,org.apache.jackrabbit.test.api.PropertyItemIsNewTest,org.apache.jackrabbit.test.api.PropertyTest,org.apache.jackrabbit.test.api.query.ElementTest,org.apache.jackrabbit.test.api.query.GetPersistentQueryPathTest,org.apache.jackrabbit.test.api.query.OrderByDateTest,org.apache.jackrabbit.test.api.query.OrderByDoubleTest,org.apache.jackrabbit.test.api.query.OrderByLongTest,org.apache.jackrabbit.test.api.query.OrderByMultiTypeTest,org.apache.jackrabbit.test.api.query.OrderByStringTest,org.apache.jackrabbit.test.api.query.SaveTest,org.apache.jackrabbit.test.api.query.SQLQueryLevel2Test,org.apache.jackrabbit.test.api.query.XPathQueryLevel2Test,org.apache.jackrabbit.test.api.ReferencesTest,org.apache.jackrabbit.test.api.SerializationTest,org.apache.jackrabbit.test.api.SessionTest,org.apache.jackrabbit.test.api.SetPropertyAssumeTypeTest,org.apache.jackrabbit.test.api.SetPropertyBooleanTest,org.apache.jackrabbit.test.api.SetPropertyCalendarTest,org.apache.jackrabbit.test.api.SetPropertyConstraintViolationExceptionTest,org.apache.jackrabbit.test.api.SetPropertyDoubleTest,org.apache.jackrabbit.test.api.SetPropertyInputStreamTest,org.apache.jackrabbit.test.api.SetPropertyLongTest,org.apache.jackrabbit.test.api.SetPropertyNodeTest,org.apache.jackrabbit.test.api.SetPropertyStringTest,org.apache.jackrabbit.test.api.SetPropertyValueTest,org.apache.jackrabbit.test.api.SetValueBinaryTest,org.apache.jackrabbit.test.api.SetValueBooleanTest,org.apache.jackrabbit.test.api.SetValueConstraintViolationExceptionTest,org.apache.jackrabbit.test.api.SetValueDateTest,org.apache.jackrabbit.test.api.SetValueDoubleTest,org.apache.jackrabbit.test.api.SetValueLongTest,org.apache.jackrabbit.test.api.SetValueReferenceTest,org.apache.jackrabbit.test.api.SetValueStringTest,org.apache.jackrabbit.test.api.SetValueValueFormatExceptionTest,org.apache.jackrabbit.test.api.SetValueVersionExceptionTest,org.apache.jackrabbit.test.api.ValueFactoryTest,org.apache.jackrabbit.test.api.WorkspaceCloneReferenceableTest,org.apache.jackrabbit.test.api.WorkspaceCloneSameNameSibsTest,org.apache.jackrabbit.test.api.WorkspaceCloneTest,org.apache.jackrabbit.test.api.WorkspaceCloneVersionableTest,org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesReferenceableTest,org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesSameNameSibsTest,org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesTest,org.apache.jackrabbit.test.api.WorkspaceCopyBetweenWorkspacesVersionableTest,org.apache.jackrabbit.test.api.WorkspaceCopyReferenceableTest,org.apache.jackrabbit.test.api.WorkspaceCopySameNameSibsTest,org.apache.jackrabbit.test.api.WorkspaceCopyTest,org.apache.jackrabbit.test.api.WorkspaceCopyVersionableTest,org.apache.jackrabbit.test.api.WorkspaceMoveReferenceableTest,org.apache.jackrabbit.test.api.WorkspaceMoveSameNameSibsTest,org.apache.jackrabbit.test.api.WorkspaceMoveTest,org.apache.jackrabbit.test.api.WorkspaceMoveVersionableTest,org.apache.jackrabbit.test.api.version.CheckinTest,org.apache.jackrabbit.test.api.version.CheckoutTest,org.apache.jackrabbit.test.api.version.GetContainingHistoryTest,org.apache.jackrabbit.test.api.version.GetCreatedTest,org.apache.jackrabbit.test.api.version.GetPredecessorsTest,org.apache.jackrabbit.test.api.version.GetReferencesNodeTest,org.apache.jackrabbit.test.api.version.GetVersionableUUIDTest,org.apache.jackrabbit.test.api.version.MergeCancelMergeTest,org.apache.jackrabbit.test.api.version.MergeCheckedoutSubNodeTest,org.apache.jackrabbit.test.api.version.MergeDoneMergeTest,org.apache.jackrabbit.test.api.version.MergeNodeIteratorTest,org.apache.jackrabbit.test.api.version.MergeNodeTest,org.apache.jackrabbit.test.api.version.MergeNonVersionableSubNodeTest,org.apache.jackrabbit.test.api.version.MergeSubNodeTest,org.apache.jackrabbit.test.api.version.OnParentVersionAbortTest,org.apache.jackrabbit.test.api.version.OnParentVersionComputeTest,org.apache.jackrabbit.test.api.version.OnParentVersionCopyTest,org.apache.jackrabbit.test.api.version.OnParentVersionIgnoreTest,org.apache.jackrabbit.test.api.version.OnParentVersionInitializeTest,org.apache.jackrabbit.test.api.version.RemoveVersionTest,org.apache.jackrabbit.test.api.version.RestoreTest,org.apache.jackrabbit.test.api.version.SessionMoveVersionExceptionTest,org.apache.jackrabbit.test.api.version.VersionGraphTest,org.apache.jackrabbit.test.api.version.VersionHistoryTest,org.apache.jackrabbit.test.api.version.VersionLabelTest,org.apache.jackrabbit.test.api.version.VersionStorageTest,org.apache.jackrabbit.test.api.version.VersionTest,org.apache.jackrabbit.test.api.version.WorkspaceMoveVersionExceptionTest,org.apache.jackrabbit.test.api.version.WorkspaceRestoreTest,org.apache.jackrabbit.test.api.lock.LockTest,org.apache.jackrabbit.test.api.query.SQLJcrPathTest,org.apache.jackrabbit.test.api.query.SQLJoinTest,org.apache.jackrabbit.test.api.query.SQLOrderByTest,org.apache.jackrabbit.test.api.query.SQLPathTest,org.apache.jackrabbit.test.api.observation.AddEventListenerTest,org.apache.jackrabbit.test.api.observation.EventIteratorTest,org.apache.jackrabbit.test.api.observation.EventTest,org.apache.jackrabbit.test.api.observation.GetRegisteredEventListenersTest,org.apache.jackrabbit.test.api.observation.LockingTest,org.apache.jackrabbit.test.api.observation.NodeAddedTest,org.apache.jackrabbit.test.api.observation.NodeMovedTest,org.apache.jackrabbit.test.api.observation.NodeRemovedTest,org.apache.jackrabbit.test.api.observation.NodeReorderTest,org.apache.jackrabbit.test.api.observation.PropertyAddedTest,org.apache.jackrabbit.test.api.observation.PropertyChangedTest,org.apache.jackrabbit.test.api.observation.PropertyRemovedTest,org.apache.jackrabbit.test.api.observation.WorkspaceOperationTest,org.apache.jackrabbit.test.api.query.TextNodeTest,org.apache.jackrabbit.test.api.SessionUUIDTest \ No newline at end of file diff --git a/source/java/org/alfresco/jcr/test/BaseJCRTest.java b/source/java/org/alfresco/jcr/test/BaseJCRTest.java new file mode 100644 index 0000000000..e222e3c77d --- /dev/null +++ b/source/java/org/alfresco/jcr/test/BaseJCRTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.test; + +import javax.jcr.Repository; + +import junit.framework.TestCase; + +import org.alfresco.jcr.repository.RepositoryFactory; +import org.alfresco.jcr.repository.RepositoryImpl; +import org.alfresco.service.cmr.repository.StoreRef; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * Base JCR Test + * + * @author David Caruana + */ +public class BaseJCRTest extends TestCase +{ + private RepositoryImpl repositoryImpl; + protected Repository repository; + protected StoreRef storeRef; + + private static ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:org/alfresco/jcr/test/test-context.xml"); + + protected String getWorkspace() + { + return storeRef.getIdentifier(); + } + + @Override + protected void setUp() throws Exception + { + storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis()); + TestData.generateTestData(applicationContext, storeRef.getIdentifier()); + repositoryImpl = (RepositoryImpl)applicationContext.getBean(RepositoryFactory.REPOSITORY_BEAN); + repositoryImpl.setDefaultWorkspace(storeRef.getIdentifier()); + repository = repositoryImpl; + } + +} diff --git a/source/java/org/alfresco/jcr/test/TestData.java b/source/java/org/alfresco/jcr/test/TestData.java new file mode 100644 index 0000000000..3a66166892 --- /dev/null +++ b/source/java/org/alfresco/jcr/test/TestData.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import org.alfresco.repo.importer.ImporterBootstrap; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.security.authentication.MutableAuthenticationDao; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.view.ImporterService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.transaction.TransactionService; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + + + +public class TestData +{ + public static final String TEST_WORKSPACE = "test"; + + /** + * Generate Test Workspace within Repository + * + * @param args + */ + public static void main(String[] args) + { + ApplicationContext context = new ClassPathXmlApplicationContext("org/alfresco/jcr/test/test-context.xml"); + generateTestData(context, TEST_WORKSPACE); + System.out.println("Generated TCK test data to workspace: " + TEST_WORKSPACE); + System.exit(0); + } + + /** + * Bootstrap Repository with JCR Test Data + * + * @param applicationContext + * @param workspaceName + */ + public static void generateTestData(ApplicationContext applicationContext, String workspaceName) + { + { + // Bootstrap Users + MutableAuthenticationDao authDAO = (MutableAuthenticationDao) applicationContext.getBean("alfDaoImpl"); + if (authDAO.userExists("superuser") == false) + { + authDAO.createUser("superuser", "".toCharArray()); + } + if (authDAO.userExists("user") == false) + { + authDAO.createUser("user", "".toCharArray()); + } + if (authDAO.userExists("anonymous") == false) + { + authDAO.createUser("anonymous", "".toCharArray()); + } + } + + try + { + AuthenticationComponent authenticationComponent = (AuthenticationComponent)applicationContext.getBean("authenticationComponent"); + authenticationComponent.setSystemUserAsCurrentUser(); + + try + { + // Bootstrap Workspace Test Data + StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, workspaceName); + + ImporterBootstrap bootstrap = new ImporterBootstrap(); + bootstrap.setAuthenticationComponent((AuthenticationComponent) applicationContext.getBean("authenticationComponent")); + bootstrap.setImporterService((ImporterService) applicationContext.getBean(ServiceRegistry.IMPORTER_SERVICE.getLocalName())); + bootstrap.setNodeService((NodeService) applicationContext.getBean(ServiceRegistry.NODE_SERVICE.getLocalName())); + bootstrap.setNamespaceService((NamespaceService) applicationContext.getBean(ServiceRegistry.NAMESPACE_SERVICE.getLocalName())); + bootstrap.setTransactionService((TransactionService) applicationContext.getBean(ServiceRegistry.TRANSACTION_SERVICE.getLocalName())); + bootstrap.setStoreUrl(storeRef.toString()); + + List views = new ArrayList(); + Properties testView = new Properties(); + testView.setProperty("path", "/"); + testView.setProperty("location", "org/alfresco/jcr/test/testData.xml"); + views.add(testView); + bootstrap.setBootstrapViews(views); + bootstrap.bootstrap(); + + // Bootstrap clears security context + authenticationComponent.setSystemUserAsCurrentUser(); + + PermissionService permissionService = (PermissionService)applicationContext.getBean(ServiceRegistry.PERMISSIONS_SERVICE.getLocalName()); + NodeService nodeService = (NodeService)applicationContext.getBean(ServiceRegistry.NODE_SERVICE.getLocalName()); + +// permissionService.setPermission(nodeService.getRootNode(storeRef), PermissionService.ALL_AUTHORITIES, PermissionService.ALL_PERMISSIONS, true); + permissionService.setPermission(nodeService.getRootNode(storeRef), "superuser", PermissionService.ALL_PERMISSIONS, true); + permissionService.setPermission(nodeService.getRootNode(storeRef), "anonymous", PermissionService.READ, true); + permissionService.setPermission(nodeService.getRootNode(storeRef), "user", PermissionService.READ, true); + permissionService.setPermission(nodeService.getRootNode(storeRef), "user", PermissionService.WRITE, true); + } + finally + { + authenticationComponent.clearCurrentSecurityContext(); + } + } + catch (RuntimeException e) + { + System.out.println("Exception: " + e); + e.printStackTrace(); + throw e; + } + } + +} diff --git a/source/java/org/alfresco/jcr/test/docview.xml b/source/java/org/alfresco/jcr/test/docview.xml new file mode 100644 index 0000000000..25fd8d1296 --- /dev/null +++ b/source/java/org/alfresco/jcr/test/docview.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/java/org/alfresco/jcr/test/sysview.xml b/source/java/org/alfresco/jcr/test/sysview.xml new file mode 100644 index 0000000000..ab7025b359 --- /dev/null +++ b/source/java/org/alfresco/jcr/test/sysview.xml @@ -0,0 +1,682 @@ + + + + jcrtest:testtype + + + sys:referenceable + mix:referenceable + + + 36110c2e-68bf-11da-98b9-375bcb5cbca6 + + + true + + + 3.141592653589793 + + + 36110c2e-68bf-11da-98b9-375bcb5cbca6 + + + 2005-09-16T19:20:05.034+01:00 + + + 90834953485278298 + + + jcrtest:test + + + workspace + + + 36110c2e-68bf-11da-98b9-375bcb5cbca6 + + + 2005-09-16T20:20:05.555+01:00 + + + test + + + + jcrtest:testtype + + + sys:referenceable + mix:referenceable + + + 36137d2f-68bf-11da-98b9-375bcb5cbca6 + + + workspace + + + 36137d2f-68bf-11da-98b9-375bcb5cbca6 + + + 36137d2f-68bf-11da-98b9-375bcb5cbca6 + + + test + + + + + jcrtest:testtype + + + sys:referenceable + mix:referenceable + + + 36137d30-68bf-11da-98b9-375bcb5cbca6 + + + workspace + + + 36137d30-68bf-11da-98b9-375bcb5cbca6 + + + 36137d30-68bf-11da-98b9-375bcb5cbca6 + + + test + + + + jcrtest:testtype + + + sys:referenceable + mix:referenceable + + + 3615c721-68bf-11da-98b9-375bcb5cbca6 + + + workspace + + + 3615c721-68bf-11da-98b9-375bcb5cbca6 + + + 3615c721-68bf-11da-98b9-375bcb5cbca6 + + + test + + + + + jcrtest:testtype + + + sys:referenceable + mix:referenceable + + + 3615c722-68bf-11da-98b9-375bcb5cbca6 + + + workspace + + + 3615c722-68bf-11da-98b9-375bcb5cbca6 + + + 3615c722-68bf-11da-98b9-375bcb5cbca6 + + + test + + + + + + jcrtest:testtype + + + sys:referenceable + mix:referenceable + + + 36183823-68bf-11da-98b9-375bcb5cbca6 + + + workspace + + + 36183823-68bf-11da-98b9-375bcb5cbca6 + + + value one + value two + value three + + + 36183823-68bf-11da-98b9-375bcb5cbca6 + + + test + + + + + cm:content + + + cm:auditable + sys:referenceable + mix:referenceable + + + 36183824-68bf-11da-98b9-375bcb5cbca6 + + + System + + + 2005-12-09T14:22:21.676Z + + + 36183824-68bf-11da-98b9-375bcb5cbca6 + + + System + + + workspace + + + dHJ1ZQ== + + + Test Content + + + test + + + 2005-12-09T14:22:21.660Z + + + + + jcrtest:testtype + + + sys:referenceable + mix:referenceable + + + 361f6416-68bf-11da-98b9-375bcb5cbca6 + + + workspace + + + 361f6416-68bf-11da-98b9-375bcb5cbca6 + + + 361f6416-68bf-11da-98b9-375bcb5cbca6 + + + test + + + + jcrtest:testtype + + + sys:referenceable + mix:referenceable + + + 3621ae07-68bf-11da-98b9-375bcb5cbca6 + + + workspace + + + a + + + 3621ae07-68bf-11da-98b9-375bcb5cbca6 + + + 3621ae07-68bf-11da-98b9-375bcb5cbca6 + + + test + + + + + jcrtest:testtype + + + sys:referenceable + mix:referenceable + + + 3621ae08-68bf-11da-98b9-375bcb5cbca6 + + + workspace + + + c + + + 3621ae08-68bf-11da-98b9-375bcb5cbca6 + + + 3621ae08-68bf-11da-98b9-375bcb5cbca6 + + + test + + + + + jcrtest:testtype + + + sys:referenceable + mix:referenceable + + + 3621ae09-68bf-11da-98b9-375bcb5cbca6 + + + workspace + + + e + + + 3621ae09-68bf-11da-98b9-375bcb5cbca6 + + + 3621ae09-68bf-11da-98b9-375bcb5cbca6 + + + test + + + + + jcrtest:testtype + + + sys:referenceable + mix:referenceable + + + 36241f0a-68bf-11da-98b9-375bcb5cbca6 + + + workspace + + + g + + + 36241f0a-68bf-11da-98b9-375bcb5cbca6 + + + 36241f0a-68bf-11da-98b9-375bcb5cbca6 + + + test + + + + + jcrtest:testtype + + + sys:referenceable + mix:referenceable + + + 36241f0b-68bf-11da-98b9-375bcb5cbca6 + + + workspace + + + b + + + 36241f0b-68bf-11da-98b9-375bcb5cbca6 + + + 36241f0b-68bf-11da-98b9-375bcb5cbca6 + + + test + + + + + jcrtest:testtype + + + sys:referenceable + mix:referenceable + + + 3626900c-68bf-11da-98b9-375bcb5cbca6 + + + workspace + + + d + + + 3626900c-68bf-11da-98b9-375bcb5cbca6 + + + 3626900c-68bf-11da-98b9-375bcb5cbca6 + + + test + + + + + jcrtest:testtype + + + sys:referenceable + mix:referenceable + + + 3628d9fd-68bf-11da-98b9-375bcb5cbca6 + + + workspace + + + f + + + 3628d9fd-68bf-11da-98b9-375bcb5cbca6 + + + 3628d9fd-68bf-11da-98b9-375bcb5cbca6 + + + test + + + + + jcrtest:testtype + + + sys:referenceable + mix:referenceable + + + 3628d9fe-68bf-11da-98b9-375bcb5cbca6 + + + workspace + + + h + + + 3628d9fe-68bf-11da-98b9-375bcb5cbca6 + + + 3628d9fe-68bf-11da-98b9-375bcb5cbca6 + + + test + + + + + jcrtest:testdoc + + + sys:referenceable + mix:referenceable + + + 362b4aff-68bf-11da-98b9-375bcb5cbca6 + + + workspace + + + Doc 1 + + + 362b4aff-68bf-11da-98b9-375bcb5cbca6 + + + 362b4aff-68bf-11da-98b9-375bcb5cbca6 + + + test + + + + jcrtest:testpara + + + sys:referenceable + mix:referenceable + + + 362d94f0-68bf-11da-98b9-375bcb5cbca6 + + + P 1 1 + + + workspace + + + 362d94f0-68bf-11da-98b9-375bcb5cbca6 + + + 362d94f0-68bf-11da-98b9-375bcb5cbca6 + + + A bit of wiffle + + + test + + + + + jcrtest:testpara + + + sys:referenceable + mix:referenceable + + + 362d94f1-68bf-11da-98b9-375bcb5cbca6 + + + P 1 2 + + + workspace + + + 362d94f1-68bf-11da-98b9-375bcb5cbca6 + + + 362d94f1-68bf-11da-98b9-375bcb5cbca6 + + + More whitterings + + + test + + + + + jcrtest:testpara + + + sys:referenceable + mix:referenceable + + + 362d94f2-68bf-11da-98b9-375bcb5cbca6 + + + P 1 3 + + + workspace + + + 362d94f2-68bf-11da-98b9-375bcb5cbca6 + + + 362d94f2-68bf-11da-98b9-375bcb5cbca6 + + + Carrot, spud, turnip and leek. + + + test + + + + + + jcrtest:testdoc + + + sys:referenceable + mix:referenceable + + + 363005f3-68bf-11da-98b9-375bcb5cbca6 + + + workspace + + + Doc 2 + + + 363005f3-68bf-11da-98b9-375bcb5cbca6 + + + 363005f3-68bf-11da-98b9-375bcb5cbca6 + + + test + + + + jcrtest:testpara + + + sys:referenceable + mix:referenceable + + + 363005f4-68bf-11da-98b9-375bcb5cbca6 + + + P 2 1 + + + workspace + + + 363005f4-68bf-11da-98b9-375bcb5cbca6 + + + 363005f4-68bf-11da-98b9-375bcb5cbca6 + + + tiger, lion + + + test + + + + + jcrtest:testpara + + + sys:referenceable + mix:referenceable + + + 363276f5-68bf-11da-98b9-375bcb5cbca6 + + + P 2 2 + + + workspace + + + 363276f5-68bf-11da-98b9-375bcb5cbca6 + + + 363276f5-68bf-11da-98b9-375bcb5cbca6 + + + biscuit, bun, cake + + + test + + + + + jcrtest:testpara + + + sys:referenceable + mix:referenceable + + + 363276f6-68bf-11da-98b9-375bcb5cbca6 + + + P 2 3 + + + workspace + + + 363276f6-68bf-11da-98b9-375bcb5cbca6 + + + 363276f6-68bf-11da-98b9-375bcb5cbca6 + + + penguin, hawk, dove + + + test + + + + + \ No newline at end of file diff --git a/source/java/org/alfresco/jcr/test/test-context.xml b/source/java/org/alfresco/jcr/test/test-context.xml new file mode 100644 index 0000000000..275822ea4f --- /dev/null +++ b/source/java/org/alfresco/jcr/test/test-context.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + org/alfresco/jcr/test/testModel.xml + + + + + diff --git a/source/java/org/alfresco/jcr/test/testContent.txt b/source/java/org/alfresco/jcr/test/testContent.txt new file mode 100644 index 0000000000..f32a5804e2 --- /dev/null +++ b/source/java/org/alfresco/jcr/test/testContent.txt @@ -0,0 +1 @@ +true \ No newline at end of file diff --git a/source/java/org/alfresco/jcr/test/testData.xml b/source/java/org/alfresco/jcr/test/testData.xml new file mode 100644 index 0000000000..49c8bf88e0 --- /dev/null +++ b/source/java/org/alfresco/jcr/test/testData.xml @@ -0,0 +1,111 @@ + + + + + + + + true + 2005-09-16T18:20:05.034Z + 3.141592653589793 + 90834953485278298 + {http://www.alfresco.org/test/jcr/1.0}test + 2005-09-16T20:20:05.555+01:00 + + + + + + + + + + + + + + + + value one + value two + value three + + + + + + Test Content + contentUrl=org/alfresco/jcr/test/testContent.txt|mimetype=text/plain|size=|encoding=UTF-8 + + + + + + + + a + + + c + + + e + + + g + + + b + + + d + + + f + + + h + + + Doc 1 + + + P 1 1 + A bit of wiffle + + + P 1 2 + More whitterings + + + P 1 3 + Carrot, spud, turnip and leek. + + + + + Doc 2 + + + P 2 1 + tiger, lion + + + P 2 2 + biscuit, bun, cake + + + P 2 3 + penguin, hawk, dove + + + + + + + + + + diff --git a/source/java/org/alfresco/jcr/test/testModel.xml b/source/java/org/alfresco/jcr/test/testModel.xml new file mode 100644 index 0000000000..75a8f640c7 --- /dev/null +++ b/source/java/org/alfresco/jcr/test/testModel.xml @@ -0,0 +1,152 @@ + + + JCR Test Model Definitions + 1.0 + + + + + + + + + + + + + + Base Test Type + sys:base + + + + sys:base + + + + + + + Test Type + jcrtest:basetesttype + + + d:boolean + + + d:date + + + d:double + + + d:long + + + d:qname + + + d:text + + + d:text + true + + + d:text + + + + + + Test Doc Type + jcrtest:basetesttype + + + d:text + + + + + + Test Doc Type + sys:base + + + d:text + + + d:text + + + + + + Test Set Properties + jcrtest:basetesttype + + + d:boolean + + + d:boolean + true + + + d:date + + + d:date + true + + + d:double + + + d:double + true + + + d:long + + + d:long + true + + + d:qname + + + d:qname + true + + + d:text + + + d:text + true + + + d:content + + + d:noderef + + + d:noderef + true + + + d:any + + + d:any + true + + + + + + + \ No newline at end of file diff --git a/source/java/org/alfresco/jcr/util/AbstractRangeIterator.java b/source/java/org/alfresco/jcr/util/AbstractRangeIterator.java new file mode 100644 index 0000000000..117b761991 --- /dev/null +++ b/source/java/org/alfresco/jcr/util/AbstractRangeIterator.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.util; + +import java.util.NoSuchElementException; + +import javax.jcr.RangeIterator; + + +/** + * Alfresco implementation of a Node Iterator + * + * @author David Caruana + */ +public abstract class AbstractRangeIterator implements RangeIterator +{ + private int position = -1; + + /** + * Construct + * + * @param context session context + * @param nodes node list + */ + public AbstractRangeIterator() + { + } + + /** + * Skip 1 position + * + * @return current position + */ + protected long skip() + { + skip(1); + return position; + } + + /* (non-Javadoc) + * @see javax.jcr.RangeIterator#skip(long) + */ + public void skip(long skipNum) + { + if (skipNum < 0) + { + throw new IllegalArgumentException("skipNum must be positive."); + } + if (position + skipNum >= getSize()) + { + throw new NoSuchElementException("Cannot skip " + skipNum + " elements from position " + getPosition() + " as only " + getSize() + " elements are available."); + } + position += skipNum; + } + + /* (non-Javadoc) + * @see javax.jcr.RangeIterator#getPosition() + */ + public long getPosition() + { + return position + 1; + } + + /* (non-Javadoc) + * @see java.util.Iterator#hasNext() + */ + public boolean hasNext() + { + return getPosition() < getSize(); + } + + /* (non-Javadoc) + * @see java.util.Iterator#remove() + */ + public void remove() + { + throw new UnsupportedOperationException(); + } + +} diff --git a/source/java/org/alfresco/jcr/util/JCRProxyFactory.java b/source/java/org/alfresco/jcr/util/JCRProxyFactory.java new file mode 100644 index 0000000000..1ce4497283 --- /dev/null +++ b/source/java/org/alfresco/jcr/util/JCRProxyFactory.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.util; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import javax.jcr.AccessDeniedException; +import javax.jcr.RepositoryException; +import javax.jcr.lock.LockException; +import javax.jcr.nodetype.ConstraintViolationException; +import javax.jcr.nodetype.NoSuchNodeTypeException; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.repo.node.integrity.IntegrityException; +import org.alfresco.service.cmr.dictionary.InvalidTypeException; +import org.alfresco.service.cmr.lock.NodeLockedException; +import org.alfresco.service.cmr.security.AuthenticationService; + + +/** + * Factory for creating a JCR Session Context Proxy + * + * The Session Context Proxy is responsible for ensuring that the appropriate Alfresco + * Repository context is setup when a method is executed within a JCR session. For + * example, the appropriate authenticated user is validated. + * + * @author David Caruana + * + */ +public class JCRProxyFactory +{ + + /** + * Create a Session Context Proxy + * + * @param target target object to wrap + * @param proxyInterface the proxy interface to export + * @param context the session context + * @return the proxied target + */ + public static Object create(Object target, Class proxyInterface, SessionImpl context) + { + InvocationHandler handler = new SessionContextInvocationHandler(target, context); + return Proxy.newProxyInstance(proxyInterface.getClassLoader(), new Class[]{proxyInterface}, handler); + } + + + /** + * Session Context Invocation Handler + * + * @author David Caruana + */ + private static class SessionContextInvocationHandler implements InvocationHandler + { + private Object target; + private SessionImpl session; + private AuthenticationService authenticationService; + + /** + * Constuct. + * + * @param instance the object instance holding the method + * @param delegateMethod the method to invoke + */ + private SessionContextInvocationHandler(Object target, SessionImpl context) + { + this.target = target; + this.session = context; + this.authenticationService = session.getRepositoryImpl().getServiceRegistry().getAuthenticationService(); + } + + /* (non-Javadoc) + * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) + */ + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + // Handle Object level methods + if (method.getName().equals("toString")) + { + return toString(); + } + else if (method.getName().equals("hashCode")) + { + return hashCode(); + } + else if (method.getName().equals("equals")) + { + if (Proxy.isProxyClass(args[0].getClass())) + { + return equals(Proxy.getInvocationHandler(args[0])); + } + return false; + } + + try + { + // establish authentication context + String username = authenticationService.getCurrentUserName(); + + try + { + // setup authentication context, if one does not exist (for example, in remote case) + if (!method.getName().equals("logout")) + { + if (username == null) + { + authenticationService.validate(session.getTicket()); + } + } + + // invoke underlying service + return method.invoke(target, args); + } + catch (InvocationTargetException e) + { + Throwable cause = e.getCause(); + throw cause; + } + finally + { + // cleanup authentication context (only if one didn't exist before) + if (username == null) + { + authenticationService.clearCurrentSecurityContext(); + } + } + } + catch(Throwable cause) + { + // Map Alfresco exceptions to JCR exceptions + if (cause instanceof IntegrityException) + { + throw new ConstraintViolationException(cause); + } + else if (cause instanceof NodeLockedException) + { + throw new LockException(cause); + } + else if (cause instanceof InvalidTypeException) + { + throw new NoSuchNodeTypeException(cause); + } + else if (cause instanceof org.alfresco.repo.security.permissions.AccessDeniedException) + { + throw new AccessDeniedException(cause); + } + else if (cause instanceof AlfrescoRuntimeException) + { + throw new RepositoryException(cause); + } + throw cause; + } + } + + @Override + public boolean equals(Object obj) + { + if (obj == this) + { + return true; + } + else if (obj == null || !(obj instanceof SessionContextInvocationHandler)) + { + return false; + } + SessionContextInvocationHandler other = (SessionContextInvocationHandler)obj; + return target.equals(other.target); + } + + @Override + public int hashCode() + { + return target.hashCode(); + } + + @Override + public String toString() + { + return target.toString(); + } + } + +} diff --git a/source/java/org/alfresco/jcr/version/VersionHistoryImpl.java b/source/java/org/alfresco/jcr/version/VersionHistoryImpl.java new file mode 100644 index 0000000000..e7b2b6f316 --- /dev/null +++ b/source/java/org/alfresco/jcr/version/VersionHistoryImpl.java @@ -0,0 +1,556 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.version; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.List; + +import javax.jcr.AccessDeniedException; +import javax.jcr.InvalidItemStateException; +import javax.jcr.Item; +import javax.jcr.ItemExistsException; +import javax.jcr.ItemNotFoundException; +import javax.jcr.ItemVisitor; +import javax.jcr.MergeException; +import javax.jcr.NoSuchWorkspaceException; +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.PathNotFoundException; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.ReferentialIntegrityException; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.UnsupportedRepositoryOperationException; +import javax.jcr.Value; +import javax.jcr.ValueFormatException; +import javax.jcr.lock.Lock; +import javax.jcr.lock.LockException; +import javax.jcr.nodetype.ConstraintViolationException; +import javax.jcr.nodetype.NoSuchNodeTypeException; +import javax.jcr.nodetype.NodeDefinition; +import javax.jcr.nodetype.NodeType; +import javax.jcr.version.Version; +import javax.jcr.version.VersionException; +import javax.jcr.version.VersionHistory; +import javax.jcr.version.VersionIterator; + +import org.alfresco.jcr.session.SessionImpl; +import org.alfresco.jcr.util.JCRProxyFactory; + + +/** + * Alfresco Implementation of a JCR Version History + * + * @author David Caruana + */ +public class VersionHistoryImpl implements VersionHistory +{ + private SessionImpl session; + private org.alfresco.service.cmr.version.VersionHistory versionHistory; + private VersionHistory proxy = null; + + + /** + * Construct + * + * @param context + * @param versionHistory + */ + public VersionHistoryImpl(SessionImpl context, org.alfresco.service.cmr.version.VersionHistory versionHistory) + { + this.session = context; + this.versionHistory = versionHistory; + } + + /** + * Get Version History Proxy + * + * @return version history proxy + */ + public VersionHistory getProxy() + { + if (proxy == null) + { + proxy = (VersionHistory)JCRProxyFactory.create(this, VersionHistory.class, session); + } + return proxy; + } + + /** + * Get Session + * + * @return session impl + */ + /*package*/ SessionImpl getSessionImpl() + { + return session; + } + + /** + * Get Version History impl + * + * @return version history impl + */ + /*package*/ org.alfresco.service.cmr.version.VersionHistory getVersionHistoryImpl() + { + return versionHistory; + } + + /* + * (non-Javadoc) + * @see javax.jcr.version.VersionHistory#getVersionableUUID() + */ + public String getVersionableUUID() throws RepositoryException + { + return versionHistory.getRootVersion().getVersionedNodeRef().getId(); + } + + /* + * (non-Javadoc) + * @see javax.jcr.version.VersionHistory#getRootVersion() + */ + public Version getRootVersion() throws RepositoryException + { + return new VersionImpl(this, versionHistory.getRootVersion()).getProxy(); + } + + /* + * (non-Javadoc) + * @see javax.jcr.version.VersionHistory#getAllVersions() + */ + public VersionIterator getAllVersions() throws RepositoryException + { + Collection versions = versionHistory.getAllVersions(); + List versionsList = new ArrayList(versions); + return new VersionListIterator(this, versionsList); + } + + /* + * (non-Javadoc) + * @see javax.jcr.version.VersionHistory#getVersion(java.lang.String) + */ + public Version getVersion(String versionName) throws VersionException, RepositoryException + { + org.alfresco.service.cmr.version.Version version = versionHistory.getVersion(versionName); + return new VersionImpl(this, version).getProxy(); + } + + public Version getVersionByLabel(String label) throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void addVersionLabel(String versionName, String label, boolean moveLabel) throws VersionException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void removeVersionLabel(String label) throws VersionException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean hasVersionLabel(String label) throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean hasVersionLabel(Version version, String label) throws VersionException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public String[] getVersionLabels() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public String[] getVersionLabels(Version version) throws VersionException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void removeVersion(String versionName) throws ReferentialIntegrityException, AccessDeniedException, UnsupportedRepositoryOperationException, VersionException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + + // Node implementation + // TODO: To support this set of methods will require the projection of all the JCR Version nodes. + // That's not simple. + + public Node addNode(String relPath) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Node addNode(String relPath, String primaryNodeTypeName) throws ItemExistsException, PathNotFoundException, NoSuchNodeTypeException, LockException, VersionException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void orderBefore(String srcChildRelPath, String destChildRelPath) throws UnsupportedRepositoryOperationException, VersionException, ConstraintViolationException, ItemNotFoundException, LockException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, Value value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, Value value, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, Value[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, Value[] values, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, String[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, String[] values, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, String value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, String value, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, InputStream value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, boolean value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, double value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, long value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, Calendar value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, Node value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Node getNode(String relPath) throws PathNotFoundException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public NodeIterator getNodes() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public NodeIterator getNodes(String namePattern) throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property getProperty(String relPath) throws PathNotFoundException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public PropertyIterator getProperties() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public PropertyIterator getProperties(String namePattern) throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Item getPrimaryItem() throws ItemNotFoundException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public String getUUID() throws UnsupportedRepositoryOperationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public int getIndex() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public PropertyIterator getReferences() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean hasNode(String relPath) throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean hasProperty(String relPath) throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean hasNodes() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean hasProperties() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public NodeType getPrimaryNodeType() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public NodeType[] getMixinNodeTypes() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean isNodeType(String nodeTypeName) throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void addMixin(String mixinName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void removeMixin(String mixinName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean canAddMixin(String mixinName) throws NoSuchNodeTypeException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public NodeDefinition getDefinition() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Version checkin() throws VersionException, UnsupportedRepositoryOperationException, InvalidItemStateException, LockException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void checkout() throws UnsupportedRepositoryOperationException, LockException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void doneMerge(Version version) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void cancelMerge(Version version) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void update(String srcWorkspaceName) throws NoSuchWorkspaceException, AccessDeniedException, LockException, InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public NodeIterator merge(String srcWorkspace, boolean bestEffort) throws NoSuchWorkspaceException, AccessDeniedException, MergeException, LockException, InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public String getCorrespondingNodePath(String workspaceName) throws ItemNotFoundException, NoSuchWorkspaceException, AccessDeniedException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean isCheckedOut() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void restore(String versionName, boolean removeExisting) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void restore(Version version, boolean removeExisting) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void restore(Version version, String relPath, boolean removeExisting) throws PathNotFoundException, ItemExistsException, VersionException, ConstraintViolationException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void restoreByLabel(String versionLabel, boolean removeExisting) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public VersionHistory getVersionHistory() throws UnsupportedRepositoryOperationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Version getBaseVersion() throws UnsupportedRepositoryOperationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Lock lock(boolean isDeep, boolean isSessionScoped) throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Lock getLock() throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void unlock() throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean holdsLock() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean isLocked() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public String getPath() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public String getName() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Item getAncestor(int depth) throws ItemNotFoundException, AccessDeniedException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Node getParent() throws ItemNotFoundException, AccessDeniedException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public int getDepth() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Session getSession() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean isNode() + { + return true; + } + + public boolean isNew() + { + return false; + } + + public boolean isModified() + { + return false; + } + + public boolean isSame(Item otherItem) throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void accept(ItemVisitor visitor) throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void save() throws AccessDeniedException, ItemExistsException, ConstraintViolationException, InvalidItemStateException, ReferentialIntegrityException, VersionException, LockException, NoSuchNodeTypeException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void refresh(boolean keepChanges) throws InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void remove() throws VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + +} diff --git a/source/java/org/alfresco/jcr/version/VersionImpl.java b/source/java/org/alfresco/jcr/version/VersionImpl.java new file mode 100644 index 0000000000..aa45abace7 --- /dev/null +++ b/source/java/org/alfresco/jcr/version/VersionImpl.java @@ -0,0 +1,515 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.version; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.List; + +import javax.jcr.AccessDeniedException; +import javax.jcr.InvalidItemStateException; +import javax.jcr.Item; +import javax.jcr.ItemExistsException; +import javax.jcr.ItemNotFoundException; +import javax.jcr.ItemVisitor; +import javax.jcr.MergeException; +import javax.jcr.NoSuchWorkspaceException; +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.PathNotFoundException; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.ReferentialIntegrityException; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.UnsupportedRepositoryOperationException; +import javax.jcr.Value; +import javax.jcr.ValueFormatException; +import javax.jcr.lock.Lock; +import javax.jcr.lock.LockException; +import javax.jcr.nodetype.ConstraintViolationException; +import javax.jcr.nodetype.NoSuchNodeTypeException; +import javax.jcr.nodetype.NodeDefinition; +import javax.jcr.nodetype.NodeType; +import javax.jcr.version.Version; +import javax.jcr.version.VersionException; +import javax.jcr.version.VersionHistory; + +import org.alfresco.jcr.item.NodeRefNodeIteratorImpl; +import org.alfresco.jcr.util.JCRProxyFactory; +import org.alfresco.service.cmr.repository.NodeRef; + + +/** + * Alfresco implementation of a JCR Version + * + * @author David Caruana + */ +public class VersionImpl implements Version +{ + private VersionHistoryImpl versionHistoryImpl; + private org.alfresco.service.cmr.version.Version version; + private Version proxy; + + + /** + * Construct + * + * @param versionHistoryImpl + * @param version + */ + public VersionImpl(VersionHistoryImpl versionHistoryImpl, org.alfresco.service.cmr.version.Version version) + { + this.versionHistoryImpl = versionHistoryImpl; + this.version = version; + } + + /** + * Get Version Proxy + * + * @return + */ + public Version getProxy() + { + if (proxy == null) + { + proxy = (Version)JCRProxyFactory.create(this, Version.class, versionHistoryImpl.getSessionImpl()); + } + return proxy; + } + + /* + * (non-Javadoc) + * @see javax.jcr.version.Version#getContainingHistory() + */ + public VersionHistory getContainingHistory() throws RepositoryException + { + return versionHistoryImpl.getProxy(); + } + + /* + * (non-Javadoc) + * @see javax.jcr.version.Version#getCreated() + */ + public Calendar getCreated() throws RepositoryException + { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(version.getCreatedDate()); + return calendar; + } + + /* + * (non-Javadoc) + * @see javax.jcr.version.Version#getSuccessors() + */ + public Version[] getSuccessors() throws RepositoryException + { + Collection successors = versionHistoryImpl.getVersionHistoryImpl().getSuccessors(version); + Version[] versions = new Version[successors.size()]; + int i = 0; + for (org.alfresco.service.cmr.version.Version sucessor : successors) + { + versions[i++] = new VersionImpl(versionHistoryImpl, sucessor).getProxy(); + } + return versions; + } + + /* + * (non-Javadoc) + * @see javax.jcr.version.Version#getPredecessors() + */ + public Version[] getPredecessors() throws RepositoryException + { + org.alfresco.service.cmr.version.Version predecessor = versionHistoryImpl.getVersionHistoryImpl().getPredecessor(version); + Version[] versions = new Version[1]; + versions[0] = new VersionImpl(versionHistoryImpl, predecessor).getProxy(); + return versions; + } + + /* + * (non-Javadoc) + * @see javax.jcr.Node#hasNodes() + */ + public boolean hasNodes() throws RepositoryException + { + return true; + } + + /* + * (non-Javadoc) + * @see javax.jcr.Node#getNodes() + */ + public NodeIterator getNodes() throws RepositoryException + { + List nodeRefs = new ArrayList(); + nodeRefs.add(version.getFrozenStateNodeRef()); + return new NodeRefNodeIteratorImpl(versionHistoryImpl.getSessionImpl(), nodeRefs); + } + + // Node implementation + // TODO: To support this set of methods will require the projection of all the JCR Version nodes. + // That's not simple. + + public Node addNode(String relPath) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Node addNode(String relPath, String primaryNodeTypeName) throws ItemExistsException, PathNotFoundException, NoSuchNodeTypeException, LockException, VersionException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void orderBefore(String srcChildRelPath, String destChildRelPath) throws UnsupportedRepositoryOperationException, VersionException, ConstraintViolationException, ItemNotFoundException, LockException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, Value value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, Value value, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, Value[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, Value[] values, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, String[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, String[] values, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, String value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, String value, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, InputStream value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, boolean value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, double value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, long value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, Calendar value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property setProperty(String name, Node value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Node getNode(String relPath) throws PathNotFoundException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public NodeIterator getNodes(String namePattern) throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Property getProperty(String relPath) throws PathNotFoundException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public PropertyIterator getProperties() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public PropertyIterator getProperties(String namePattern) throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Item getPrimaryItem() throws ItemNotFoundException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public String getUUID() throws UnsupportedRepositoryOperationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public int getIndex() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public PropertyIterator getReferences() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean hasNode(String relPath) throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean hasProperty(String relPath) throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean hasProperties() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public NodeType getPrimaryNodeType() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public NodeType[] getMixinNodeTypes() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean isNodeType(String nodeTypeName) throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void addMixin(String mixinName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void removeMixin(String mixinName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean canAddMixin(String mixinName) throws NoSuchNodeTypeException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public NodeDefinition getDefinition() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Version checkin() throws VersionException, UnsupportedRepositoryOperationException, InvalidItemStateException, LockException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void checkout() throws UnsupportedRepositoryOperationException, LockException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void doneMerge(Version version) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void cancelMerge(Version version) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void update(String srcWorkspaceName) throws NoSuchWorkspaceException, AccessDeniedException, LockException, InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public NodeIterator merge(String srcWorkspace, boolean bestEffort) throws NoSuchWorkspaceException, AccessDeniedException, MergeException, LockException, InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public String getCorrespondingNodePath(String workspaceName) throws ItemNotFoundException, NoSuchWorkspaceException, AccessDeniedException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean isCheckedOut() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void restore(String versionName, boolean removeExisting) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void restore(Version version, boolean removeExisting) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void restore(Version version, String relPath, boolean removeExisting) throws PathNotFoundException, ItemExistsException, VersionException, ConstraintViolationException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void restoreByLabel(String versionLabel, boolean removeExisting) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public VersionHistory getVersionHistory() throws UnsupportedRepositoryOperationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Version getBaseVersion() throws UnsupportedRepositoryOperationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Lock lock(boolean isDeep, boolean isSessionScoped) throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Lock getLock() throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void unlock() throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean holdsLock() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean isLocked() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public String getPath() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public String getName() throws RepositoryException + { + return version.getVersionLabel(); + } + + public Item getAncestor(int depth) throws ItemNotFoundException, AccessDeniedException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Node getParent() throws ItemNotFoundException, AccessDeniedException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public int getDepth() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public Session getSession() throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public boolean isNode() + { + return true; + } + + public boolean isNew() + { + return false; + } + + public boolean isModified() + { + return false; + } + + public boolean isSame(Item otherItem) throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void accept(ItemVisitor visitor) throws RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void save() throws AccessDeniedException, ItemExistsException, ConstraintViolationException, InvalidItemStateException, ReferentialIntegrityException, VersionException, LockException, NoSuchNodeTypeException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void refresh(boolean keepChanges) throws InvalidItemStateException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + public void remove() throws VersionException, LockException, ConstraintViolationException, RepositoryException + { + throw new UnsupportedRepositoryOperationException(); + } + + +} diff --git a/source/java/org/alfresco/jcr/version/VersionListIterator.java b/source/java/org/alfresco/jcr/version/VersionListIterator.java new file mode 100644 index 0000000000..98747fb0da --- /dev/null +++ b/source/java/org/alfresco/jcr/version/VersionListIterator.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.jcr.version; + +import java.util.List; + +import javax.jcr.version.Version; +import javax.jcr.version.VersionIterator; + +import org.alfresco.jcr.util.AbstractRangeIterator; + + +/** + * Alfresco implementation of a Property Iterator + * + * @author David Caruana + */ +public class VersionListIterator extends AbstractRangeIterator + implements VersionIterator +{ + private VersionHistoryImpl versionHistory; + private List versions; + + + /** + * Construct + * + * @param context session context + * @param versions version list + */ + public VersionListIterator(VersionHistoryImpl versionHistory, List versions) + { + this.versionHistory = versionHistory; + this.versions = versions; + } + + /* + * (non-Javadoc) + * @see javax.jcr.version.VersionIterator#nextVersion() + */ + public Version nextVersion() + { + long position = skip(); + org.alfresco.service.cmr.version.Version version = versions.get((int)position); + return new VersionImpl(versionHistory, version).getProxy(); + } + + /* (non-Javadoc) + * @see javax.jcr.RangeIterator#getSize() + */ + public long getSize() + { + return versions.size(); + } + + /* (non-Javadoc) + * @see java.util.Iterator#next() + */ + public Object next() + { + return nextVersion(); + } + +} diff --git a/source/web/WEB-INF/JCRTCK/web.xml b/source/web/WEB-INF/JCRTCK/web.xml new file mode 100644 index 0000000000..fbec618d6f --- /dev/null +++ b/source/web/WEB-INF/JCRTCK/web.xml @@ -0,0 +1,30 @@ + + + + + + Alfresco JCR TCK Deployment + + Alfresco JCR TCK Deployment + + + contextConfigLocation + + classpath:org/alfresco/jcr/test/test-context.xml + + Spring config file locations + + + + org.springframework.web.context.ContextLoaderListener + + + + Repository Startup + org.alfresco.jcr.tck.RepositoryStartupServlet + 1 + + +