By Smitheo
Jan 03 2025
Manually updating AEM content can be time-consuming and error-prone. Sling Pipes provides a powerful, streamlined approach for performing CRUD operations on AEM repositories. In this post, we explore how Sling Pipes enables developers to efficiently automate content updates with just a few lines of code, reducing complexity while improving accuracy. Discover practical use cases and learn how to enhance your AEM workflows today.
You are still updating content manually? Try out Sling pipes.
Several weeks ago I saw some tweet about how Adobe guys are using Sling pipes for updating their AEM content, so I was curious and I tried it out.
Let's see what I have found out...
Sling pipes are a simple tool for executing CRUD operations over resources in the AEM repository. But is it powerful enough to replace your groovy scripts? Let's discover it in the following examples.
Just to get your attention, let's say in the past you have made some bad decisions during the development of some of your components and now you need to update all content with that component. With Sling Pipes that is very easy.
Example
plumber.newPipe(resourceResolver)
.echo("/content/yoursite")
.$("nt:unstructured[sling:resourceType=projectName/components/content/imageSlider]")
.write("autoplay", true)
.run()
5 lines? Yes, you can set the autoplay property to true in all your content with the image slider component in 5 lines, and I'm pretty much sure you can't do that so simply with your groovy code, or you can do it manually in crx/de for the whole day...
So what the hell is happening in this code, what are these methods?!
I will explain it later in more detail but in tl;dr;
Still interested? Let me explain to you Sling pipes in more detail.
Sling pipes is a toolset for doing extract-transform-load operations by chaining proven code blocks. A sling pipe is essentially a sling resource stream, encapsulating well-known sling operations.
There are 3 types of pipes:
The reader pipes are used to get resources.
Base pipe: echo(path) Used to receive resources on the given path. Let's say you want to get "/content/we-retail" resource.
plumber.newPipe(resourceResolver)
.echo("/content/we-retail")
.run()
XPathPipe: xpath(expr) Used to receive resources with a given xpath query Let's say you want to get all nt:unstructured resources with heroimage component under "/content/we-retail".
plumber.newPipe(resourceResolver)
.xpath("/jcr:root/content/we-retail//element(*, nt:unstructured)
[(@sling:resourceType = 'weretail/components/content/heroimage')]")
.run()
Sling Query Pipes Wrapper for executing Sling Query methods in sling pipe way.
Here are more details about Sling Query
The same example as the previous one, let's say you want to get all resources with the heroimage component under "/content/we-retail".
plumber.newPipe(resourceResolver)
.echo("/content/we-retail")
.$("nt:unstructured[sling:resourceType=weretail/components/content/heroimage]")
.run()
The writer pipes are used to modify resources.
Write Pipe: write(conf) Used to write given nodes and properties to current input resource.
Let's say you want to create/update property "foo" with value "bar" and property "foo2" with value "bar2".
Please let me know if is it possible to write different values except for strings, like long, date, boolean?
plumber.newPipe(resourceResolver)
.echo("/content/we-retail")
.write('foo','bar','foo2','bar2')
.run()
PathPipe: mkdir(expr) Used to create resources on the given location.
Let's you want to create resource "master" on "/content/we-retail/master" location. This will create "sling:Folder" node.
Please let me know if is it possible to specify different jcr:primaryType?
plumber.newPipe(resourceResolver)
.mkdir("/content/we-retail/master")
.run()
MovePipe: mv(expr) Moves resource or property to the target path.
Following example will move master resource from "/content/we-retail/master" to "/content/we-retail/language-master".
plumber.newPipe(resourceResolver)
.echo("/content/we-retail/master")
.mv("/content/we-retail/language-master")
.run()
RemovePipe: rm() Removes resources or property on the specific location.
The following example will remove the master resource on the given path "/content/we-retail/master".
plumber.newPipe(resourceResolver)
.echo("/content/we-retail/master")
.rm()
.run()
PackagePipe: pkg(expr) Creates a package and adds current resources as a filter.
The following example will package all we retail content
plumber.newPipe(resourceResolver)
.echo("/content/we-retail")
.pkg("/etc/packages/we-retail-content.zip")
.run()
Okay, how to run all this in your AEM project? You can configure and execute pipes with Java, groovy console, HTTP, or Jmx
You need to add "org.apache.sling.query" & "org.apache.sling.pipes" as dependencies and you need to embed them as part of your bundle or install them as separate bundles (my recommended way).
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.query</artifactId>
<version>4.0.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.pipes</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<plugin>
<groupId>org.apache.jackrabbit</groupId>
<artifactId>filevault-package-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<allowIndexDefinitions>true</allowIndexDefinitions>
<packageType>mixed</packageType>
<group>pipes</group>
<embeddeds>
<!--sling pipes related-->
<embedded>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.query</artifactId>
<target>/apps/pipes-vendor-packages/application/install</target>
</embedded>
<embedded>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.pipes</artifactId>
<target>/apps/pipes-vendor-packages/application/install</target>
</embedded>
</embeddeds>
</configuration>
</plugin>
You can write in groovy console.
def plumber = getService("org.apache.sling.pipes.Plumber")
plumber.newPipe(resourceResolver)
.echo("/content/yoursite")
.$("nt:unstructured[sling:resourceType=projectName/components/content/imageSlider]")
.write("autoplay", true)
.run()
You can write in your Java class.
@Component(service = SlingPipesExamples.class, immediate = true)
public class SlingPipesExamples {
private static final Logger logger = LoggerFactory.getLogger(SlingPipesExamples.class);
private static final String SERVICE_USER = "sling-pipes-service-user";
@Reference
private ResourceResolverFactory resourceResolverFactory;
@Reference
private Plumber plumber;
public void example() {
try (final ResourceResolver resourceResolver = this.resourceResolverFactory
.getServiceResourceResolver(this.getAuthenticationInfo())) {
PipeBuilder pipeBuilder = this.plumber.newPipe(resourceResolver);
// your logic with pipes
} catch (final LoginException e) {
logger.error("Exception during creating service resource resolver ", e);
}
}
private Map<String, Object> getAuthenticationInfo() {
return Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, SERVICE_USER);
}
}
You can create a resource on a specific location e.g. "/etc/sling-pipes/example" and execute it with the HTTP request.
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:description="Create directory pipe"
jcr:primaryType="sling:OrderedFolder"
sling:resourceType="slingPipes/mkdir"
expr="/content/sling-pipes/foo"/>
Basically that's what I have discovered so far. In general, I like Sling Pipes, especially how with a few lines of code you can do a lot of stuff. Didn't try with some more complex examples but I guess it would be almost the same.
For more details take a look at offical documentation.
Let me know if for mkdir pipe is possible to specify a different jcr:primaryType? If it's only "sling:Folder" then 👎
Also for write pipe, is it possible to write in a property some value different than String?
I hope that you learn something and that you will give it a try Sling Pipes. Let me know what you think and what are your experiences with Sling Pipes :)
Leverage best practices and tailored solutions to drive success. Contact us today to learn how we can help optimize your Adobe Experience Manager implementation.