You are here

NodeService

Provides an API for managing nodes.
Information NodeService
Support Status Full Support
Architecture Information Platform Architecture
Description Nodes are the fundamental data structure in Alfresco Content Services. All content that is stored is represented by a node data structure, which contains content metadata and is persisted in a database (such as PostgreSQL). The content referenced by the node is stored as a *.bin file in a content store (such as the file system, S3, encrypted or other content store). Every node in the system is referenced by a NodeRef, which is made up of the content store protocol, the content store name, and the Universal Unique Identifier (UUID) of the content, for example: workspace://SpacesStore/ccb906ba-a768-4ccb-8b26-515119e1efdc.

Generally nodes are of two main types, a content node (cm:content), or a folder node (cm:folder). Folders can contain child nodes. Note that each content store will have a root node, and all other nodes in the store will be children of the root node.

The NodeService provides an extensive API for managing nodes. Functionality includes:
  • Adding aspects, children, properties, associations
  • Getting aspects, children, properties, associations
  • Removing aspects, children, properties, associations
  • Creating and deleting stores
  • Creating and deleting nodes
  • Checking for existence of a node
  • Get available content stores
  • Moving nodes

The NodeService makes extensive use of NodeRefs to reference the node of interest.

Setting content for node

Since Alfresco 6.2 the cm:content property cannot be set using the NodeService. Any code that uses NodeService methods setProperties, addProperties, createNode, addAspect, or setProperty to set the content property need to be refactored or designed to use the ContentWriter service instead. The ContentWriter can be obtained from ContentService and used to set the content for a node.

There are ways to get around this security check:

Add full class names as a comma separated list to a global property:

contentPropertyRestrictions.whitelist=org.alfresco.Class1,org.alfresco.Class2

Disable the ContentPropertyRestrictionInterceptor fully by setting a global property:

contentPropertyRestrictions.enabled=false

However, it's better to refactor the code by using the ContentWriter.

Deleting nodes

Since Alfresco 4.1.1 the alf_node.node_deleted database column has been replaced by a system type (sys:deleted) and an aspect (sys:pendingDelete). While the sys:deleted type will never be visible to client code, the sys:pendingDelete aspect will be. Any custom code that attempts to modify behaviour during node deletion may need to be adjusted.

Changes made in 4.1.1 introduced comprehensive behaviour policy callbacks (i.e. event handlers) for all associations during node deletion. The following node policies are available for node deletion:

  • BeforeDeleteNodePolicy
  • BeforeArchiveNodePolicy
  • OnDeleteNodePolicy
  • BeforeDeleteChildAssociationPolicy
  • OnDeleteChildAssociationPolicy
  • BeforeDeleteAssociationPolicy
  • OnDeleteAssociationPolicy

The association (peer and child) policies are now fired reliably for all associations within the node hierarchy being deleted. For examples of their usage, see: org.alfresco.repo.model.ml.MultilingualDocumentAspect.

Once NodeService.deleteNode is called:

  • It is impossible to add or remove associations to or from any node in the hierarchy being deleted. This includes attempted changes from any source including changes attempted by custom code reacting to before- or on-delete callbacks.
  • All nodes in the hierarchy will temporarily have the sys:pendingDelete aspect applied. Custom code can using NodeService.hasAspect to discover if a node is about to be deleted.
  • It is impossible to add new nodes or link other nodes into any node in the hierarchy being deleted. Any attempt to do so will be treated as a concurrency violation since custom code should not be attempting this from callbacks during the node deletion.
  • All associations, with the notable exception of the primary parent-child links, will be removed even if node archival is taking place. Node archival now only preserves the core parent-child associations and discards all other associations after making the relevant callbacks. Custom code must use the association deletion callbacks to remove nodes or aspects that might violate model integrity constraints in the archived hierarchy.

A good example of the changes is in the handling of the cm:copiedFrom aspect. Copied nodes have an aspect cm:copiedfrom, which has a mandatory association to the original source node. When either the source or copy is deleted the aspect has to be removed. See org.alfresco.repo.copy.CopyServiceImpl.beforeDeleteOriginalAssociation for how the association deletion is detected in order to ensure that the aspect is removed from the copied node.

Deployment - App Server Deploy as AMP or Simple Module (JAR) package.
Deployment - SDK Project Use SDK archetypes to produce AMP or Simple Module.
Java API Java API
Java example
// Getting a NodeRef from its path
/////////////////////////////////////
StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
ResultSet rs = serviceRegistry.getSearchService().query(storeRef, SearchService.LANGUAGE_LUCENE, "PATH:\"/app:company_home/app:user_homes/sys:boris/cm:mypics\"");
NodeRef companyHomeNodeRef = null;
try {
  if (rs.length() == 0) {
      throw new AlfrescoRuntimeException("Didn't find Company Home");
  }
  companyHomeNodeRef = rs.getNodeRef(0);
}
finally {
  rs.close();
}

// Getting a file name from a NodeRef
/////////////////////////////////////
String fileName = (String) serviceRegistry.getNodeService().getProperty(nodeRef, ContentModel.PROP_NAME);

// Reading a property of a node
// The property may come from an aspect or not. 
// You will probably want to cast to the appropriate type.
/////////////////////////////////////
QName PROP_QNAME_MY_PROPERTY = QName.createQName("custom.model", "myProperty");
value = serviceRegistry.getNodeService().getProperty(nodeRef, PROP_QNAME_MY_PROPERTY);

// Updating a property of a node
// The property may come from an aspect or not.
/////////////////////////////////////
QName PROP_QNAME_MY_PROPERTY = QName.createQName("custom.model", "myProperty");
serviceRegistry.getNodeService().setProperty(nodeRef, PROP_QNAME_MY_PROPERTY, value);

// Getting the parent of a NodeRef
/////////////////////////////////////
ChildAssociationRef childAssociationRef = serviceRegistry.getNodeService().getPrimaryParent(nodeRef);
NodeRef parent = childAssociationRef.getParentRef();

// Adding an aspect to a node
// Supposing the "MyAspect" aspect defines a "myProperty" 
// property in the "custom.model" namespace.
/////////////////////////////////////
QName CUSTOM_ASPECT_QNAME = QName.createQName("custom.model", "MyAspect");
QName PROP_QNAME_MY_PROPERTY = QName.createQName("custom.model", "myProperty");
Map<QName,Serializable> aspectValues = new HashMap<QName,Serializable>();
aspectValues.put(PROP_QNAME_MY_PROPERTY, value);
serviceRegistry.getNodeService().addAspect(nodeRef, CUSTOM_ASPECT_QNAME, aspectValues);

// Checking whether a node has a given aspect
/////////////////////////////////////
QName CUSTOM_ASPECT_QNAME = QName.createQName("custom.model", "MyAspect");
boolean hasAspect = serviceRegistry.getNodeService().hasAspect(node, CUSTOM_ASPECT_QNAME);

// Looping through children of a NodeRef
/////////////////////////////////////
List<ChildAssociationRef> children = serviceRegistry.getNodeService().getChildAssocs(companyHome);
for (ChildAssociationRef childAssoc : children) {
  NodeRef childNodeRef = childAssoc.getChildRef();
  // Use childNodeRef here.
}

// Creating a child association between two existing NodeRef
/////////////////////////////////////
QName PROP_QNAME_MY_CHILD_ASSOCIATION = QName.createQName("custom.model", "myChildAssociation");
serviceRegistry.getNodeService().addChild(parentNodeRef, childNodeRef, PROP_QNAME_MY_CHILD_ASSOCIATION, PROP_QNAME_MY_CHILD_ASSOCIATION);

// Creating an association between two NodeRef
/////////////////////////////////////
QName PROP_QNAME_MY_ASSOCIATION = QName.createQName("custom.model", "myAssociation");
serviceRegistry.getNodeService().createAssociation(sourceNodeRef, targetNodeRef, PROP_QNAME_MY_ASSOCIATION);

// Setting the type of a node
/////////////////////////////////////
QName PROP_QNAME_MY_TYPE = QName.createQName("custom.model", "myType");
serviceRegistry.getNodeService().setType(finalOriginal, MY_TYPE);

// Getting the MIME type of a node
/////////////////////////////////////
ContentData contentData = (ContentData) serviceRegistry.getNodeService().getProperty(nodeRef, ContentModel.PROP_CONTENT);
String originalMimeType = contentData.getMimetype();

// Adding a category to a node
/////////////////////////////////////
ArrayList<NodeRef> categories = new ArrayList<NodeRef>(1);
categories.add(categoryNode);
if (!serviceRegistry.getNodeService().hasAspect(targetNode, ContentModel.ASPECT_GEN_CLASSIFIABLE) {
  HashMap<QName, Serializable> props = new HashMap<QName, Serializable>();
  props.put(ContentModel.PROP_CATEGORIES, categories);
  serviceRegistry.getNodeService().addAspect(targetNode, ContentModel.ASPECT_GEN_CLASSIFIABLE, props);
} else {
  serviceRegistry.getNodeService().setProperty(targetNode, ContentModel.PROP_CATEGORIES, categories);
}

// Getting the categories of a node
/////////////////////////////////////
List<NodeRef> categories = (List<NodeRef>) serviceRegistry.getNodeService().getProperty(nodeRef, ContentModel.PROP_CATEGORIES);

// Deleting a node for real (not recycle bin)
/////////////////////////////////////
serviceRegistry.getNodeService().addAspect(nodeRef, ContentModel.ASPECT_TEMPORARY, null);
serviceRegistry.getNodeService().deleteNode(nodeRef);         
More Information

Sending feedback to the Alfresco documentation team

You don't appear to have JavaScript enabled in your browser. With JavaScript enabled, you can provide feedback to us using our simple form. Here are some instructions on how to enable JavaScript in your web browser.