You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by np...@apache.org on 2017/11/11 22:21:34 UTC

[sling-org-apache-sling-pipes] branch master updated: SLING-7206 add outputs to pipebuilder api

This is an automated email from the ASF dual-hosted git repository.

npeltier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-pipes.git


The following commit(s) were added to refs/heads/master by this push:
     new a5070eb  SLING-7206 add outputs to pipebuilder api
a5070eb is described below

commit a5070eb1607dbf4bcdadf2a2f413a937384c8114
Author: npeltier <pe...@gmail.com>
AuthorDate: Sat Nov 11 23:17:10 2017 +0100

    SLING-7206 add outputs to pipebuilder api
    
    - introduces ExecutionResult, wrapping writer,
    - introduces output api,
    - allow writers not to work only from a servlet context
---
 .../org/apache/sling/pipes/ExecutionResult.java    | 83 ++++++++++++++++++++++
 .../java/org/apache/sling/pipes/OutputWriter.java  | 68 ++++++++++++------
 .../java/org/apache/sling/pipes/PipeBuilder.java   | 15 +++-
 src/main/java/org/apache/sling/pipes/Plumber.java  | 10 ++-
 .../sling/pipes/internal/CustomJsonWriter.java     | 62 ++++++++++++++--
 .../apache/sling/pipes/internal/CustomWriter.java  | 70 ------------------
 ...ultOutputWriter.java => DefaultJsonWriter.java} | 41 +++++++----
 .../org/apache/sling/pipes/internal/NopWriter.java |  7 +-
 .../sling/pipes/internal/PipeBuilderImpl.java      | 27 +++++--
 .../apache/sling/pipes/internal/PlumberImpl.java   | 51 +++++++------
 .../sling/pipes/internal/PlumberServlet.java       |  6 +-
 .../org/apache/sling/pipes/PipeBuilderTest.java    | 47 +++++++++---
 .../sling/pipes/internal/FilterPipeTest.java       | 22 +++---
 .../sling/pipes/internal/PlumberServletTest.java   |  4 +-
 .../sling/pipes/internal/ReferencePipeTest.java    | 12 ++--
 .../pipes/internal/slingQuery/ClosestPipeTest.java |  9 +--
 .../pipes/internal/slingQuery/ParentPipeTest.java  |  9 +--
 .../pipes/internal/slingQuery/ParentsPipeTest.java |  6 +-
 .../internal/slingQuery/SiblingsPipeTest.java      |  9 +--
 .../org/apache/sling/pipes/it/PipeBuilderIT.java   | 14 ++--
 .../apache/sling/pipes/it/PlumberServletIT.java    | 61 ++++++++++++++++
 .../SLING-INF/jcr_root/content/fruits.json         |  2 +
 .../jcr_root/etc/pipes-it/another-list.json        |  7 ++
 23 files changed, 440 insertions(+), 202 deletions(-)

diff --git a/src/main/java/org/apache/sling/pipes/ExecutionResult.java b/src/main/java/org/apache/sling/pipes/ExecutionResult.java
new file mode 100644
index 0000000..c0cd43c
--- /dev/null
+++ b/src/main/java/org/apache/sling/pipes/ExecutionResult.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.apache.sling.pipes;
+
+import org.apache.sling.api.resource.Resource;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * holds results of the execution
+ */
+public class ExecutionResult {
+
+    /**
+     * *not* meant to hold the all the paths, just a set that is emptied each time
+     * it's persisted.
+     */
+    Set<String> currentPathSet;
+
+    OutputWriter writer;
+
+    /**
+     * Constructor
+     * @param writer output writer around which to create the result
+     */
+    public ExecutionResult(OutputWriter writer) {
+        this.writer = writer;
+        currentPathSet = new HashSet<>();
+    }
+
+    /**
+     * Add a resource to the results
+     * @param resource resource to add
+     */
+    public void addResultItem(Resource resource) {
+        writer.write(resource);
+        currentPathSet.add(resource.getPath());
+    }
+
+    /**
+     * Empty the current path set
+     */
+    public void emptyCurrentSet(){
+        currentPathSet.clear();
+    }
+
+    /**
+     * return currentPathSet
+     * @return current path set
+     */
+    public Collection<String> getCurrentPathSet() {
+        return currentPathSet;
+    }
+
+    /**
+     * amount of changed items
+     * @return total size of changed items
+     */
+    public long size(){
+        return writer.size;
+    }
+
+    @Override
+    public String toString() {
+        return writer.toString();
+    }
+}
diff --git a/src/main/java/org/apache/sling/pipes/OutputWriter.java b/src/main/java/org/apache/sling/pipes/OutputWriter.java
index 26d4386..9db2981 100644
--- a/src/main/java/org/apache/sling/pipes/OutputWriter.java
+++ b/src/main/java/org/apache/sling/pipes/OutputWriter.java
@@ -17,15 +17,14 @@
 package org.apache.sling.pipes;
 
 import java.io.IOException;
-
-import javax.json.JsonException;
+import java.io.Writer;
 
 import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.SlingHttpServletResponse;
 import org.apache.sling.api.resource.Resource;
 
 /**
- * defines how pipe's output get written to a servlet response
+ * defines how pipe's output get written to a servlet response or output stream
  */
 public abstract class OutputWriter {
 
@@ -37,12 +36,14 @@ public abstract class OutputWriter {
 
     public static final int NB_MAX = 10;
 
-    protected int size;
+    protected long size;
 
-    protected int max = NB_MAX;
+    protected long max = NB_MAX;
 
     protected Pipe pipe;
 
+    protected Writer writer;
+
     /**
      *
      * @param request current request
@@ -55,31 +56,51 @@ public abstract class OutputWriter {
      * @param request request from which writer will output
      * @param response response on which writer will output
      * @throws IOException error handling streams
-     * @throws JsonException in case invalid json is written
      */
-    public void init(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException, JsonException {
-        max = request.getParameter(PARAM_SIZE) != null ? Integer.parseInt(request.getParameter(PARAM_SIZE)) : NB_MAX;
-        if (max < 0) {
-            max = Integer.MAX_VALUE;
+    public void init(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
+        if (request.getParameter(PARAM_SIZE) != null) {
+            setMax(Integer.parseInt(request.getParameter(PARAM_SIZE)));
         }
-        initInternal(request, response);
+        setWriter(response.getWriter());
+        initResponse(response);
+        starts();
     }
 
     /**
+     * Specifically init the response
+     * @param response response on which to write
+     */
+    protected abstract void initResponse(SlingHttpServletResponse response);
+
+    /**
      * Init the writer, writes beginning of the output
-     * @param request request from which writer will output
-     * @param response response on which writer will output
-     * @throws IOException error handling streams
-     * @throws JsonException in case invalid json is written
      */
-    protected abstract void initInternal(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException, JsonException;
+    public abstract void starts();
+
+    /**
+     * Setter for max (will put to max if value is negative)
+     * @param max positive max value to set
+     */
+    public void setMax(int max) {
+        this.max = max;
+        if (max < 0) {
+            this.max = Integer.MAX_VALUE;
+        }
+    }
+
+    /**
+     * Set the writer
+     * @param writer writer on which to write output
+     */
+    public void setWriter(Writer writer) {
+        this.writer = writer;
+    }
 
     /**
      * Write a given resource
      * @param resource resource that will be written
-     * @throws JsonException in case write fails
      */
-    public void write(Resource resource) throws JsonException {
+    public void write(Resource resource) {
         if (size++ < max) {
             writeItem(resource);
         }
@@ -88,16 +109,14 @@ public abstract class OutputWriter {
     /**
      * Write a given resource
      * @param resource resource that will be written
-     * @throws JsonException in case write fails
      */
-    protected abstract void writeItem(Resource resource) throws JsonException;
+    protected abstract void writeItem(Resource resource);
 
     /**
      * writes the end of the output
-     * @throws JsonException in case invalid json is written
      */
 
-    public abstract void ends() throws JsonException;
+    public abstract void ends();
 
     /**
      * Setter
@@ -106,4 +125,9 @@ public abstract class OutputWriter {
     public void setPipe(Pipe pipe) {
         this.pipe = pipe;
     }
+
+    @Override
+    public String toString() {
+        return writer.toString();
+    }
 }
diff --git a/src/main/java/org/apache/sling/pipes/PipeBuilder.java b/src/main/java/org/apache/sling/pipes/PipeBuilder.java
index 51135bf..0d9a4ac 100644
--- a/src/main/java/org/apache/sling/pipes/PipeBuilder.java
+++ b/src/main/java/org/apache/sling/pipes/PipeBuilder.java
@@ -20,6 +20,7 @@ import org.apache.sling.api.resource.PersistenceException;
 import org.apache.sling.event.jobs.Job;
 import org.osgi.annotation.versioning.ProviderType;
 
+import javax.json.Json;
 import java.util.Map;
 import java.util.Set;
 
@@ -209,6 +210,14 @@ public interface PipeBuilder {
      */
     PipeBuilder conf(Object... properties) throws IllegalAccessException;
 
+
+    /**
+     * add outputs passed key
+     * @param keys properties of the outputs resource to output
+     * @return current instance of PipeBuilder
+     */
+    PipeBuilder outputs(String ... keys);
+
     /**
      * builds a configured pipe. The configuration will be placed in a balanced tree under <code>/var/pipes</code>
      * @return Created (not executed) Pipe instance.
@@ -229,7 +238,7 @@ public interface PipeBuilder {
      * @return set of resource path, output of the pipe execution
      * @throws Exception exceptions thrown by the build or the pipe execution itself
      */
-    Set<String> run() throws Exception;
+    ExecutionResult run() throws Exception;
 
     /**
      * allow execution of a pipe, with more parameter
@@ -237,7 +246,7 @@ public interface PipeBuilder {
      * @return set of resource path, output of the pipe execution
      * @throws Exception in case something goes wrong with pipe execution
      */
-    Set<String> run(Map bindings) throws Exception;
+    ExecutionResult run(Map bindings) throws Exception;
 
     /**
      * allow execution of a pipe, with more parameter
@@ -245,7 +254,7 @@ public interface PipeBuilder {
      * @return set of resource path, output of the pipe execution
      * @throws Exception in case something goes wrong with pipe execution
      */
-    Set<String> runWith(Object... bindings) throws Exception;
+    ExecutionResult runWith(Object... bindings) throws Exception;
 
     /**
      * run a pipe asynchronously
diff --git a/src/main/java/org/apache/sling/pipes/Plumber.java b/src/main/java/org/apache/sling/pipes/Plumber.java
index 202257c..bb8fcbb 100644
--- a/src/main/java/org/apache/sling/pipes/Plumber.java
+++ b/src/main/java/org/apache/sling/pipes/Plumber.java
@@ -17,11 +17,9 @@
 package org.apache.sling.pipes;
 
 import java.util.Map;
-import java.util.Set;
 
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
-import org.apache.sling.api.resource.ResourceResolverFactory;
 import org.apache.sling.event.jobs.Job;
 import org.osgi.annotation.versioning.ProviderType;
 
@@ -57,9 +55,9 @@ public interface Plumber {
      * @param writer output of the pipe
      * @param save in case that pipe writes anything, wether the plumber should save changes or not
      * @throws Exception in case execution fails
-     * @return set of paths of output resources
+     * @return instance of <code>ExecutionResult</code>
      */
-    Set<String> execute(ResourceResolver resolver, String path, Map bindings, OutputWriter writer, boolean save) throws Exception;
+    ExecutionResult execute(ResourceResolver resolver, String path, Map bindings, OutputWriter writer, boolean save) throws Exception;
 
     /**
      * Executes a given pipe
@@ -69,9 +67,9 @@ public interface Plumber {
      * @param writer output of the pipe
      * @param save in case that pipe writes anything, wether the plumber should save changes or not
      * @throws Exception in case execution fails
-     * @return set of paths of output resources
+     * @return instance of <code>ExecutionResult</code>
      */
-    Set<String> execute(ResourceResolver resolver, Pipe pipe, Map bindings, OutputWriter writer, boolean save) throws Exception;
+    ExecutionResult execute(ResourceResolver resolver, Pipe pipe, Map bindings, OutputWriter writer, boolean save) throws Exception;
 
     /**
      * Registers
diff --git a/src/main/java/org/apache/sling/pipes/internal/CustomJsonWriter.java b/src/main/java/org/apache/sling/pipes/internal/CustomJsonWriter.java
index 380be2e..32596f8 100644
--- a/src/main/java/org/apache/sling/pipes/internal/CustomJsonWriter.java
+++ b/src/main/java/org/apache/sling/pipes/internal/CustomJsonWriter.java
@@ -16,17 +16,40 @@
  */
 package org.apache.sling.pipes.internal;
 
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.json.JsonValue;
+
 import org.apache.commons.lang3.StringUtils;
 import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.pipes.BasePipe;
+import org.apache.sling.pipes.Pipe;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * same thing as CustomWriter, but uses a JSON object passed as writer parameter
+ * writes current resource, dubbing a eventual parameter or given child resource "writer" property/value pairs, allowing
+ * expressions
  */
-public class CustomJsonWriter extends CustomWriter {
+public class CustomJsonWriter extends DefaultJsonWriter {
+    Logger log = LoggerFactory.getLogger(CustomJsonWriter.class);
+
+    public static final String PATH_KEY = "path";
+
+    public static final String PARAM_WRITER = "writer";
+
+    Map<String, Object> customOutputs;
 
-    private static final Logger log = LoggerFactory.getLogger(CustomJsonWriter.class);
+    CustomJsonWriter() {
+    }
+
+    CustomJsonWriter(Writer writer) {
+        super(writer);
+    }
 
     @Override
     public boolean handleRequest(SlingHttpServletRequest request) {
@@ -38,7 +61,38 @@ public class CustomJsonWriter extends CustomWriter {
             } catch(Exception e){
                 log.error("requested json writer can't be parsed", e);
             }
+        } else {
+            Resource resource = request.getResource().getChild(PARAM_WRITER);
+            return resource != null;
         }
         return false;
     }
-}
+
+    @Override
+    public void setPipe(Pipe pipe) {
+        super.setPipe(pipe);
+        if (customOutputs == null){
+            customOutputs = new HashMap<>();
+            customOutputs.putAll(pipe.getResource().getChild(PARAM_WRITER).adaptTo(ValueMap.class));
+            for (String ignoredKey : BasePipe.IGNORED_PROPERTIES) {
+                customOutputs.remove(ignoredKey);
+            }
+        }
+    }
+
+    @Override
+    public void writeItem(Resource resource) {
+        jsonWriter.writeStartObject();
+        jsonWriter.write(PATH_KEY,resource.getPath());
+        for (Map.Entry<String, Object> entry : customOutputs.entrySet()){
+            Object o = pipe.getBindings().instantiateObject((String)entry.getValue());
+            if ( o instanceof JsonValue ) {
+                jsonWriter.write(entry.getKey(),(JsonValue) o);
+            }
+            else {
+                jsonWriter.write(entry.getKey(), o.toString());
+            }
+        }
+        jsonWriter.writeEnd();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/pipes/internal/CustomWriter.java b/src/main/java/org/apache/sling/pipes/internal/CustomWriter.java
deleted file mode 100644
index 209e9a8..0000000
--- a/src/main/java/org/apache/sling/pipes/internal/CustomWriter.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * 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.apache.sling.pipes.internal;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.json.JsonException;
-import javax.json.JsonValue;
-
-import org.apache.sling.api.SlingHttpServletRequest;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.api.resource.ValueMap;
-import org.apache.sling.pipes.BasePipe;
-
-/**
- * writes current resource, dubbing a given child resource "writer" property/value pairs, allowing expressions
- */
-public class CustomWriter extends DefaultOutputWriter {
-    public static final String PATH_KEY = "path";
-
-    public static final String PARAM_WRITER = "writer";
-
-
-    Map<String, Object> customOutputs;
-
-    @Override
-    public boolean handleRequest(SlingHttpServletRequest request) {
-        Resource resource = request.getResource().getChild(PARAM_WRITER);
-        if (resource != null){
-            customOutputs = new HashMap<>();
-            customOutputs.putAll(resource.adaptTo(ValueMap.class));
-            for (String ignoredKey : BasePipe.IGNORED_PROPERTIES){
-                customOutputs.remove(ignoredKey);
-            }
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public void writeItem(Resource resource) throws JsonException {
-        writer.writeStartObject();
-        writer.write(PATH_KEY,resource.getPath());
-        for (Map.Entry<String, Object> entry : customOutputs.entrySet()){
-            Object o = pipe.getBindings().instantiateObject((String)entry.getValue());
-            if ( o instanceof JsonValue ) {
-               writer.write(entry.getKey(),(JsonValue) o);
-            }
-            else {
-                writer.write(entry.getKey(), o.toString());
-            }
-        }
-        writer.writeEnd();
-    }
-}
diff --git a/src/main/java/org/apache/sling/pipes/internal/DefaultOutputWriter.java b/src/main/java/org/apache/sling/pipes/internal/DefaultJsonWriter.java
similarity index 63%
rename from src/main/java/org/apache/sling/pipes/internal/DefaultOutputWriter.java
rename to src/main/java/org/apache/sling/pipes/internal/DefaultJsonWriter.java
index 83e8d05..6e4e1f5 100644
--- a/src/main/java/org/apache/sling/pipes/internal/DefaultOutputWriter.java
+++ b/src/main/java/org/apache/sling/pipes/internal/DefaultJsonWriter.java
@@ -17,9 +17,9 @@
 package org.apache.sling.pipes.internal;
 
 import java.io.IOException;
+import java.io.Writer;
 
 import javax.json.Json;
-import javax.json.JsonException;
 import javax.json.stream.JsonGenerator;
 
 import org.apache.sling.api.SlingHttpServletRequest;
@@ -28,11 +28,18 @@ import org.apache.sling.api.resource.Resource;
 import org.apache.sling.pipes.OutputWriter;
 
 /**
- * default output writer with size and output resources' path
+ * default output writer, that outputs JSON with size and output resources' path
  */
-public class DefaultOutputWriter extends OutputWriter {
+public class DefaultJsonWriter extends OutputWriter {
 
-    protected JsonGenerator writer;
+    protected JsonGenerator jsonWriter;
+
+    DefaultJsonWriter(){
+    }
+
+    DefaultJsonWriter(Writer writer){
+        setWriter(writer);
+    }
 
     @Override
     public boolean handleRequest(SlingHttpServletRequest request) {
@@ -40,24 +47,28 @@ public class DefaultOutputWriter extends OutputWriter {
     }
 
     @Override
-    protected void initInternal(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException, JsonException {
+    protected void initResponse(SlingHttpServletResponse response){
         response.setCharacterEncoding("utf-8");
         response.setContentType("application/json");
-        writer = Json.createGenerator(response.getWriter());
-        writer.writeStartObject();
-        writer.writeStartArray(KEY_ITEMS);
     }
 
     @Override
-    public void writeItem(Resource resource) throws JsonException {
-        writer.write(resource.getPath());
+    public void starts() {
+        jsonWriter = Json.createGenerator(writer);
+        jsonWriter.writeStartObject();
+        jsonWriter.writeStartArray(KEY_ITEMS);
+    }
+
+    @Override
+    public void writeItem(Resource resource) {
+        jsonWriter.write(resource.getPath());
     }
 
     @Override
-    public void ends() throws JsonException {
-        writer.writeEnd();
-        writer.write(KEY_SIZE,size);
-        writer.writeEnd();
-        writer.flush();
+    public void ends() {
+        jsonWriter.writeEnd();
+        jsonWriter.write(KEY_SIZE,size);
+        jsonWriter.writeEnd();
+        jsonWriter.flush();
     }
 }
diff --git a/src/main/java/org/apache/sling/pipes/internal/NopWriter.java b/src/main/java/org/apache/sling/pipes/internal/NopWriter.java
index ae44684..eaa1a76 100644
--- a/src/main/java/org/apache/sling/pipes/internal/NopWriter.java
+++ b/src/main/java/org/apache/sling/pipes/internal/NopWriter.java
@@ -30,7 +30,12 @@ public class NopWriter extends OutputWriter {
     }
 
     @Override
-    protected void initInternal(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
+    protected void initResponse(SlingHttpServletResponse response) {
+        //nop
+    }
+
+    @Override
+    public void starts() {
         //nop
     }
 
diff --git a/src/main/java/org/apache/sling/pipes/internal/PipeBuilderImpl.java b/src/main/java/org/apache/sling/pipes/internal/PipeBuilderImpl.java
index 2f7a5fb..e7141aa 100644
--- a/src/main/java/org/apache/sling/pipes/internal/PipeBuilderImpl.java
+++ b/src/main/java/org/apache/sling/pipes/internal/PipeBuilderImpl.java
@@ -32,6 +32,8 @@ import org.apache.sling.pipes.internal.slingQuery.SiblingsPipe;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.json.Json;
+import java.io.StringWriter;
 import java.util.*;
 
 import static org.apache.sling.jcr.resource.JcrResourceConstants.NT_SLING_FOLDER;
@@ -50,6 +52,8 @@ public class PipeBuilderImpl implements PipeBuilder {
 
     List<Step> steps;
 
+    Map outputs;
+
     Step containerStep = new Step(ContainerPipe.RESOURCE_TYPE);
 
     Step currentStep = containerStep;
@@ -289,6 +293,13 @@ public class PipeBuilderImpl implements PipeBuilder {
     }
 
     @Override
+    public PipeBuilder outputs(String... keys) {
+        outputs = new HashMap();
+        writeToMap(outputs, keys);
+        return this;
+    }
+
+    @Override
     public Pipe build() throws PersistenceException {
         return build(buildRandomPipePath());
     }
@@ -313,6 +324,9 @@ public class PipeBuilderImpl implements PipeBuilder {
     @Override
     public Pipe build(String path) throws PersistenceException {
         Resource pipeResource = persistStep(path, NT_SLING_FOLDER, containerStep);
+        if (outputs != null){
+            ResourceUtil.getOrCreateResource(resolver, path + "/" + CustomJsonWriter.PARAM_WRITER, outputs, NT_SLING_FOLDER, false);
+        }
         int index = 0;
         for (Step step : steps){
             String name = StringUtils.isNotBlank(step.name) ? step.name : DEFAULT_NAMES.length > index ? DEFAULT_NAMES[index] : Integer.toString(index);
@@ -325,12 +339,12 @@ public class PipeBuilderImpl implements PipeBuilder {
     }
 
     @Override
-    public Set<String> run() throws Exception {
+    public ExecutionResult run() throws Exception {
         return run(null);
     }
 
     @Override
-    public Set<String> runWith(Object... bindings) throws Exception {
+    public ExecutionResult runWith(Object... bindings) throws Exception {
         checkArguments(bindings);
         Map bindingsMap = new HashMap();
         writeToMap(bindingsMap, bindings);
@@ -338,9 +352,12 @@ public class PipeBuilderImpl implements PipeBuilder {
     }
 
     @Override
-    public Set<String> run(Map bindings) throws Exception {
+    public ExecutionResult run(Map bindings) throws Exception {
+        StringWriter stringWriter = new StringWriter();
+        DefaultJsonWriter writer = outputs != null ? new CustomJsonWriter(stringWriter) : new DefaultJsonWriter(stringWriter);
+        writer.starts();
         Pipe pipe = this.build();
-        return plumber.execute(resolver, pipe, bindings,  new NopWriter() , true);
+        return plumber.execute(resolver, pipe, bindings,  writer , true);
     }
 
     @Override
@@ -361,4 +378,4 @@ public class PipeBuilderImpl implements PipeBuilder {
             properties.put(SLING_RESOURCE_TYPE_PROPERTY, type);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/pipes/internal/PlumberImpl.java b/src/main/java/org/apache/sling/pipes/internal/PlumberImpl.java
index 2d6f0c8..5e51bf5 100644
--- a/src/main/java/org/apache/sling/pipes/internal/PlumberImpl.java
+++ b/src/main/java/org/apache/sling/pipes/internal/PlumberImpl.java
@@ -28,11 +28,9 @@ import java.util.Collections;
 import java.util.Date;
 import java.util.GregorianCalendar;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import javax.jcr.RepositoryException;
 
@@ -52,7 +50,15 @@ import org.apache.sling.distribution.SimpleDistributionRequest;
 import org.apache.sling.event.jobs.Job;
 import org.apache.sling.event.jobs.JobManager;
 import org.apache.sling.event.jobs.consumer.JobConsumer;
-import org.apache.sling.pipes.*;
+import org.apache.sling.pipes.BasePipe;
+import org.apache.sling.pipes.ContainerPipe;
+import org.apache.sling.pipes.ExecutionResult;
+import org.apache.sling.pipes.OutputWriter;
+import org.apache.sling.pipes.Pipe;
+import org.apache.sling.pipes.PipeBindings;
+import org.apache.sling.pipes.PipeBuilder;
+import org.apache.sling.pipes.Plumber;
+import org.apache.sling.pipes.ReferencePipe;
 import org.apache.sling.pipes.internal.slingQuery.ChildrenPipe;
 import org.apache.sling.pipes.internal.slingQuery.ClosestPipe;
 import org.apache.sling.pipes.internal.slingQuery.FindPipe;
@@ -174,7 +180,7 @@ public class PlumberImpl implements Plumber, JobConsumer {
     }
 
     @Override
-    public Set<String> execute(ResourceResolver resolver, String path, Map additionalBindings, OutputWriter writer, boolean save) throws Exception {
+    public ExecutionResult execute(ResourceResolver resolver, String path, Map additionalBindings, OutputWriter writer, boolean save) throws Exception {
         Resource pipeResource = resolver.getResource(path);
         Pipe pipe = getPipe(pipeResource);
         if (pipe == null) {
@@ -187,7 +193,7 @@ public class PlumberImpl implements Plumber, JobConsumer {
     }
 
     @Override
-    public Set<String> execute(ResourceResolver resolver, Pipe pipe, Map additionalBindings, OutputWriter writer, boolean save) throws Exception {
+    public ExecutionResult execute(ResourceResolver resolver, Pipe pipe, Map additionalBindings, OutputWriter writer, boolean save) throws Exception {
         try {
             if (additionalBindings != null && pipe instanceof ContainerPipe){
                 pipe.getBindings().addBindings(additionalBindings);
@@ -200,22 +206,21 @@ public class PlumberImpl implements Plumber, JobConsumer {
             writeStatus(pipe, STATUS_STARTED);
             resolver.commit();
 
-            Set<String> set = new HashSet<>();
+            ExecutionResult result = new ExecutionResult(writer);
             for (Iterator<Resource> it = pipe.getOutput(); it.hasNext();){
                 Resource resource = it.next();
                 if (resource != null) {
                     log.debug("[{}] retrieved {}", pipe.getName(), resource.getPath());
-                    writer.write(resource);
-                    set.add(resource.getPath());
-                    persist(resolver, pipe, set, resource);
+                    result.addResultItem(resource);
+                    persist(resolver, pipe, result, resource);
                 }
             }
             if (save && pipe.modifiesContent()) {
-                persist(resolver, pipe, set, null);
+                persist(resolver, pipe, result, null);
             }
             log.info("[{}] done executing.", pipe.getName());
             writer.ends();
-            return set;
+            return result;
         } finally {
             writeStatus(pipe, STATUS_FINISHED);
             resolver.commit();
@@ -226,27 +231,31 @@ public class PlumberImpl implements Plumber, JobConsumer {
      * Persists pipe change if big enough, or ended, and eventually distribute changes
      * @param resolver resolver to use
      * @param pipe pipe at the origin of the changes,
-     * @param paths paths that have been changed,
+     * @param result execution result object,
      * @param currentResource if running, null if ended
      * @throws PersistenceException in case save fails
      */
-    protected void persist(ResourceResolver resolver, Pipe pipe, Set<String> paths, Resource currentResource) throws Exception {
+    protected void persist(ResourceResolver resolver, Pipe pipe, ExecutionResult result, Resource currentResource) throws Exception {
         if  (pipe.modifiesContent() && resolver.hasChanges() && !pipe.isDryRun()){
-            if (currentResource == null || paths.size() % configuration.bufferSize() == 0){
+            if (currentResource == null || result.size() % configuration.bufferSize() == 0){
                 log.info("[{}] saving changes...", pipe.getName());
                 writeStatus(pipe, currentResource == null ? STATUS_FINISHED : currentResource.getPath());
                 resolver.commit();
+                if (currentResource == null && distributor != null && StringUtils.isNotBlank(pipe.getDistributionAgent())) {
+                    log.info("a distribution agent is configured, will try to distribute the changes");
+                    DistributionRequest request = new SimpleDistributionRequest(DistributionRequestType.ADD, true, result.getCurrentPathSet().toArray(new String[result.getCurrentPathSet().size()]));
+                    DistributionResponse response = distributor.distribute(pipe.getDistributionAgent(), resolver, request);
+                    log.info("distribution response : {}", response);
+                }
+                if (result.size() > configuration.bufferSize()){
+                    //avoid too big foot print
+                    result.emptyCurrentSet();
+                }
                 if (configuration.sleep() > 0){
                     log.debug("sleeping for {}ms", configuration.sleep());
                     Thread.sleep(configuration.sleep());
                 }
             }
-            if (currentResource == null && distributor != null && StringUtils.isNotBlank(pipe.getDistributionAgent())) {
-                log.info("a distribution agent is configured, will try to distribute the changes");
-                DistributionRequest request = new SimpleDistributionRequest(DistributionRequestType.ADD, true, paths.toArray(new String[paths.size()]));
-                DistributionResponse response = distributor.distribute(pipe.getDistributionAgent(), resolver, request);
-                log.info("distribution response : {}", response);
-            }
         }
     }
 
@@ -313,4 +322,4 @@ public class PlumberImpl implements Plumber, JobConsumer {
         }
         return JobResult.FAILED;
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/pipes/internal/PlumberServlet.java b/src/main/java/org/apache/sling/pipes/internal/PlumberServlet.java
index a95c5cb..f1db5ab 100644
--- a/src/main/java/org/apache/sling/pipes/internal/PlumberServlet.java
+++ b/src/main/java/org/apache/sling/pipes/internal/PlumberServlet.java
@@ -21,7 +21,6 @@ import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
-import javax.json.JsonException;
 import javax.servlet.Servlet;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletResponse;
@@ -154,10 +153,9 @@ public class PlumberServlet extends SlingAllMethodsServlet {
      * @param response response writers will point to
      * @return instance of the created writer
      * @throws IOException bad handling of I/O streams,
-     * @throws JsonException bad handling of json output
      */
-    OutputWriter getWriter(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException, JsonException {
-        OutputWriter[] candidates = new OutputWriter[]{new CustomJsonWriter(), new CustomWriter(), new DefaultOutputWriter()};
+    OutputWriter getWriter(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
+        OutputWriter[] candidates = new OutputWriter[]{new CustomJsonWriter(), new DefaultJsonWriter()};
         for (OutputWriter candidate : candidates) {
             if (candidate.handleRequest(request)) {
                 candidate.init(request, response);
diff --git a/src/test/java/org/apache/sling/pipes/PipeBuilderTest.java b/src/test/java/org/apache/sling/pipes/PipeBuilderTest.java
index 8d55e7c..205f9dc 100644
--- a/src/test/java/org/apache/sling/pipes/PipeBuilderTest.java
+++ b/src/test/java/org/apache/sling/pipes/PipeBuilderTest.java
@@ -16,14 +16,22 @@
  */
 package org.apache.sling.pipes;
 
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.pipes.internal.JsonUtil;
 import org.junit.Test;
 
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Set;
+import java.util.stream.Collectors;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 public class PipeBuilderTest extends AbstractPipeTest {
     @Test
@@ -39,8 +47,8 @@ public class PipeBuilderTest extends AbstractPipeTest {
     public void run() throws Exception {
         String lemonPath = "/content/fruits/lemon";
         PipeBuilder lemonBuilder = plumber.newPipe(context.resourceResolver());
-        Set<String> paths = lemonBuilder.mkdir(lemonPath).run();
-        assertTrue("returned set should contain lemon path", paths.contains(lemonPath));
+        ExecutionResult result = lemonBuilder.mkdir(lemonPath).run();
+        assertTrue("returned set should contain lemon path", result.getCurrentPathSet().contains(lemonPath));
         assertNotNull("there should be a lemon created", context.resourceResolver().getResource(lemonPath));
     }
 
@@ -65,16 +73,18 @@ public class PipeBuilderTest extends AbstractPipeTest {
     @Test
     public void bindings() throws Exception {
         PipeBuilder defaultNames = plumber.newPipe(context.resourceResolver());
-        Set<String> paths = defaultNames
+        ExecutionResult result = defaultNames
                 .echo(PATH_FRUITS)
                 .children("nt:unstructured")
                 .grep("slingPipesFilter_test","${two.worm}")
                 .children("nt:unstructured#isnota")
                 .children("nt:unstructured").name("thing")
-                .write("jcr:path", "${path.thing}").run();
-        assertEquals("There should be 3 resources", 3, paths.size());
+                .write("jcr:path", "${path.thing}")
+                .run();
+        assertEquals("There should be 3 resources", 3, result.size());
         String pea = "/content/fruits/apple/isnota/pea";
         String carrot = "/content/fruits/apple/isnota/carrot";
+        Collection<String> paths = result.getCurrentPathSet();
         assertTrue("the paths should contain " + pea, paths.contains(pea));
         assertTrue("the paths should contain " + carrot, paths.contains(carrot));
         for (String path : paths){
@@ -87,9 +97,28 @@ public class PipeBuilderTest extends AbstractPipeTest {
     public void additionalBindings() throws Exception {
         Map bindings = new HashMap<>();
         bindings.put("testedPath", PATH_FRUITS);
-        Set<String> paths = plumber.newPipe(context.resourceResolver()).echo("${testedPath}").run(bindings);
+        ExecutionResult result = plumber.newPipe(context.resourceResolver()).echo("${testedPath}").run(bindings);
+        Collection<String> paths = result.getCurrentPathSet();
         assertTrue("paths should contain implemented testedPath after run(bindings) is executed", paths.contains(PATH_FRUITS));
-        paths = plumber.newPipe(context.resourceResolver()).echo("${testedPath}").runWith("testedPath", PATH_FRUITS);
+        paths = plumber.newPipe(context.resourceResolver())
+                .echo("${testedPath}").runWith("testedPath", PATH_FRUITS)
+                .getCurrentPathSet();
         assertTrue("paths should contain implemented testedPath after runWith is executed", paths.contains(PATH_FRUITS));
     }
+
+    @Test
+    public void testOutputs() throws Exception {
+        ExecutionResult result = plumber.newPipe(context.resourceResolver())
+                                        .echo(PATH_APPLE + "/isnota")
+                                        .children("").name("vegetable")
+                                        .outputs("name","Good ${vegetable['jcr:title']}")
+                                        .run();
+        JsonObject object = JsonUtil.parseObject(result.toString());
+        Collection<String> names = object.getJsonArray("items").getValuesAs(JsonObject.class)
+                .stream()
+                    .map(o -> o.getString("name"))
+                    .collect(Collectors.toList());
+        Collection<String> expected = Arrays.asList(new String[] {"Good Pea", "Good Carrot", "Good Plum"});
+        assertTrue("all transformed items should be here", CollectionUtils.intersection(names, expected).size() == 3);
+    }
 }
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/pipes/internal/FilterPipeTest.java b/src/test/java/org/apache/sling/pipes/internal/FilterPipeTest.java
index 26fd634..5f16ba2 100644
--- a/src/test/java/org/apache/sling/pipes/internal/FilterPipeTest.java
+++ b/src/test/java/org/apache/sling/pipes/internal/FilterPipeTest.java
@@ -16,13 +16,6 @@
  */
 package org.apache.sling.pipes.internal;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import java.util.Iterator;
-import java.util.Set;
-
 import org.apache.commons.lang3.StringUtils;
 import org.apache.sling.api.resource.ModifiableValueMap;
 import org.apache.sling.api.resource.PersistenceException;
@@ -32,6 +25,13 @@ import org.apache.sling.pipes.AbstractPipeTest;
 import org.apache.sling.pipes.Pipe;
 import org.junit.Test;
 
+import java.util.Collection;
+import java.util.Iterator;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 /**
  * testing different kind of filters
  */
@@ -106,15 +106,13 @@ public class FilterPipeTest extends AbstractPipeTest {
 
     @Test
     public void testPropertyRegexp() throws Exception {
-        Set<String> outputs = plumber.newPipe(context.resourceResolver())
+        Collection<String> outputs = plumber.newPipe(context.resourceResolver())
             .echo(PATH_APPLE)
-            .grep("jcr:description","https://en.wikipedia.org").run();
+            .grep("jcr:description","https://en.wikipedia.org").run().getCurrentPathSet();
         assertEquals("there should be an item", 1, outputs.size());
         outputs = plumber.newPipe(context.resourceResolver())
                 .echo(PATH_APPLE)
-                .grep("jcr:description",".*https://en.wikipedia.org.*").run();
+                .grep("jcr:description",".*https://en.wikipedia.org.*").run().getCurrentPathSet();
         assertEquals("there should be an item", 1, outputs.size());
-
     }
-
 }
diff --git a/src/test/java/org/apache/sling/pipes/internal/PlumberServletTest.java b/src/test/java/org/apache/sling/pipes/internal/PlumberServletTest.java
index c0628db..1f9d08f 100644
--- a/src/test/java/org/apache/sling/pipes/internal/PlumberServletTest.java
+++ b/src/test/java/org/apache/sling/pipes/internal/PlumberServletTest.java
@@ -152,7 +152,7 @@ public class PlumberServletTest extends AbstractPipeTest {
         for (int i = 0; i < array.size(); i++) {
             JsonObject object = array.getJsonObject(i);
             assertNotNull("there should be an object returned at each time", object);
-            String path = object.getString(CustomWriter.PATH_KEY);
+            String path = object.getString(CustomJsonWriter.PATH_KEY);
             assertNotNull("the string path should be returned for each item, containing the path of the resource");
             String pathLength = object.getString(pathLengthParam);
             assertNotNull("there should be a pathLength param, as specified in the writer", pathLength);
@@ -205,7 +205,7 @@ public class PlumberServletTest extends AbstractPipeTest {
         when(request.getResource()).thenReturn(resource);
         when(request.getParameter(PlumberServlet.PARAM_PATH)).thenReturn(pathParam);
         when(request.getParameter(PlumberServlet.PARAM_BINDINGS)).thenReturn(bindings);
-        when(request.getParameter(CustomWriter.PARAM_WRITER)).thenReturn(writer);
+        when(request.getParameter(CustomJsonWriter.PARAM_WRITER)).thenReturn(writer);
         when(request.getParameter(BasePipe.DRYRUN_KEY)).thenReturn(dryRun);
         when(request.getParameter(OutputWriter.PARAM_SIZE)).thenReturn(size);
         return request;
diff --git a/src/test/java/org/apache/sling/pipes/internal/ReferencePipeTest.java b/src/test/java/org/apache/sling/pipes/internal/ReferencePipeTest.java
index d6cbd44..9df9a9d 100644
--- a/src/test/java/org/apache/sling/pipes/internal/ReferencePipeTest.java
+++ b/src/test/java/org/apache/sling/pipes/internal/ReferencePipeTest.java
@@ -16,9 +16,6 @@
  */
 package org.apache.sling.pipes.internal;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.pipes.AbstractPipeTest;
 import org.apache.sling.pipes.Pipe;
@@ -26,9 +23,12 @@ import org.apache.sling.pipes.ReferencePipe;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Set;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 /**
  * testing references
@@ -68,10 +68,10 @@ public class ReferencePipeTest  extends AbstractPipeTest {
         Map bindings = new HashMap();
         bindings.put("fruit", newFruit);
         Pipe pipe = plumber.newPipe(context.resourceResolver()).echo(PATH_FRUITS + "/${fruit}").build();
-        Set<String> paths = plumber.newPipe(context.resourceResolver())
+        Collection<String> paths = plumber.newPipe(context.resourceResolver())
                 .pipe(ReferencePipe.RESOURCE_TYPE)
                 .expr(pipe.getResource().getPath())
-                .run(bindings);
+                .run(bindings).getCurrentPathSet();
         assertTrue("paths should contain new path", paths.contains(newPath));
     }
 }
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/pipes/internal/slingQuery/ClosestPipeTest.java b/src/test/java/org/apache/sling/pipes/internal/slingQuery/ClosestPipeTest.java
index 24e4cd6..296e876 100644
--- a/src/test/java/org/apache/sling/pipes/internal/slingQuery/ClosestPipeTest.java
+++ b/src/test/java/org/apache/sling/pipes/internal/slingQuery/ClosestPipeTest.java
@@ -19,17 +19,18 @@ package org.apache.sling.pipes.internal.slingQuery;
 import org.apache.sling.pipes.AbstractPipeTest;
 import org.junit.Test;
 
-import java.util.Set;
+import java.util.Collection;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 public class ClosestPipeTest extends AbstractPipeTest {
 
     @Test
     public void testClosest() throws Exception {
-        Set<String> outputs = plumber.newPipe(context.resourceResolver())
+        Collection<String> outputs = plumber.newPipe(context.resourceResolver())
                 .echo(SAME_COLOR)
-                .closest("[color=green]").run();
+                .closest("[color=green]").run().getCurrentPathSet();
         assertEquals("there should be 1 output", 1, outputs.size());
         assertTrue("there should be pea", outputs.contains(PATH_PEA));
     }
diff --git a/src/test/java/org/apache/sling/pipes/internal/slingQuery/ParentPipeTest.java b/src/test/java/org/apache/sling/pipes/internal/slingQuery/ParentPipeTest.java
index 492cf0a..fbecefe 100644
--- a/src/test/java/org/apache/sling/pipes/internal/slingQuery/ParentPipeTest.java
+++ b/src/test/java/org/apache/sling/pipes/internal/slingQuery/ParentPipeTest.java
@@ -19,16 +19,17 @@ package org.apache.sling.pipes.internal.slingQuery;
 import org.apache.sling.pipes.AbstractPipeTest;
 import org.junit.Test;
 
-import java.util.Set;
+import java.util.Collection;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 public class ParentPipeTest extends AbstractPipeTest {
     @Test
     public void testParent() throws Exception {
-        Set<String> outputs = plumber.newPipe(context.resourceResolver())
+        Collection<String> outputs = plumber.newPipe(context.resourceResolver())
                 .echo(SAME_COLOR)
-                .parent().run();
+                .parent().run().getCurrentPathSet();
         assertEquals("there should be 1 outputs", 1, outputs.size());
         assertTrue("there should be pea", outputs.contains(PATH_PEA));
     }
diff --git a/src/test/java/org/apache/sling/pipes/internal/slingQuery/ParentsPipeTest.java b/src/test/java/org/apache/sling/pipes/internal/slingQuery/ParentsPipeTest.java
index 7d5c71c..f902e50 100644
--- a/src/test/java/org/apache/sling/pipes/internal/slingQuery/ParentsPipeTest.java
+++ b/src/test/java/org/apache/sling/pipes/internal/slingQuery/ParentsPipeTest.java
@@ -19,7 +19,7 @@ package org.apache.sling.pipes.internal.slingQuery;
 import org.apache.sling.pipes.AbstractPipeTest;
 import org.junit.Test;
 
-import java.util.Set;
+import java.util.Collection;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -28,9 +28,9 @@ public class ParentsPipeTest extends AbstractPipeTest {
 
     @Test
     public void testParents() throws Exception {
-        Set<String> outputs = plumber.newPipe(context.resourceResolver())
+        Collection<String> outputs = plumber.newPipe(context.resourceResolver())
                 .echo(SAME_COLOR)
-                .parents("[color=green]").run();
+                .parents("[color=green]").run().getCurrentPathSet();
         assertEquals("there should be 2 outputs", 2, outputs.size());
         assertTrue("there should be apple", outputs.contains(PATH_APPLE));
         assertTrue("there should be pea", outputs.contains(PATH_PEA));
diff --git a/src/test/java/org/apache/sling/pipes/internal/slingQuery/SiblingsPipeTest.java b/src/test/java/org/apache/sling/pipes/internal/slingQuery/SiblingsPipeTest.java
index afb7985..d67360b 100644
--- a/src/test/java/org/apache/sling/pipes/internal/slingQuery/SiblingsPipeTest.java
+++ b/src/test/java/org/apache/sling/pipes/internal/slingQuery/SiblingsPipeTest.java
@@ -19,17 +19,18 @@ package org.apache.sling.pipes.internal.slingQuery;
 import org.apache.sling.pipes.AbstractPipeTest;
 import org.junit.Test;
 
-import java.util.Set;
+import java.util.Collection;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 public class SiblingsPipeTest extends AbstractPipeTest{
 
     @Test
     public void testSiblings() throws Exception {
-        Set<String> outputs = plumber.newPipe(context.resourceResolver())
+        Collection<String> outputs = plumber.newPipe(context.resourceResolver())
                 .echo(PATH_CARROT)
-                .siblings("[color=green]").run();
+                .siblings("[color=green]").run().getCurrentPathSet();
         assertEquals("there should be 1 outputs", 1, outputs.size());
         assertTrue("there should be pea", outputs.contains(PATH_PEA));
     }
diff --git a/src/test/java/org/apache/sling/pipes/it/PipeBuilderIT.java b/src/test/java/org/apache/sling/pipes/it/PipeBuilderIT.java
index ec7b8eb..aa674fc 100644
--- a/src/test/java/org/apache/sling/pipes/it/PipeBuilderIT.java
+++ b/src/test/java/org/apache/sling/pipes/it/PipeBuilderIT.java
@@ -18,8 +18,6 @@
  */
 package org.apache.sling.pipes.it;
 
-import java.util.Set;
-
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.pipes.Pipe;
 import org.junit.Test;
@@ -30,6 +28,8 @@ import org.ops4j.pax.exam.spi.reactors.PerClass;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.Collection;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -46,7 +46,7 @@ public class PipeBuilderIT extends PipesTestSupport {
         final String CONTENT = "/content/traverse/test";
         try (ResourceResolver resolver = resolver()) {
             mkdir(resolver, CONTENT);
-            Set<String> results = plumber.newPipe(resolver).echo(CONTENT).traverse().run();
+            Collection<String> results = plumber.newPipe(resolver).echo(CONTENT).traverse().run().getCurrentPathSet();
             LOGGER.info("Following results are found {}", results);
             assertTrue("should contain former test", results.contains(CONTENT));
         }
@@ -60,7 +60,7 @@ public class PipeBuilderIT extends PipesTestSupport {
         try (ResourceResolver resolver = resolver()) {
             mkdir(resolver, CONTENT);
             mkdir(resolver, TARGET);
-            Set<String> results = plumber.newPipe(resolver).echo(CONTENT).mv(TARGET_PATH).run();
+            Collection<String> results = plumber.newPipe(resolver).echo(CONTENT).mv(TARGET_PATH).run().getCurrentPathSet();
             LOGGER.info("Following results are found {}", results);
             assertTrue("mv return should be the moved item", results.contains(TARGET_PATH));
         }
@@ -76,9 +76,9 @@ public class PipeBuilderIT extends PipesTestSupport {
                 plumber.newPipe(resolver).mkdir(path).write("xpathTestStatus", "testing").run();
             }
             final String query = String.format("/jcr:root%s//element(*,nt:base)[@xpathTestStatus]", ROOT);
-            Set<String> results = plumber.newPipe(resolver).xpath(query).run();
+            Collection<String> results = plumber.newPipe(resolver).xpath(query).run().getCurrentPathSet();
             assertEquals("xpath query should return as many items as we wrote", NB_ITEMS, results.size());
-            results = plumber.newPipe(resolver).echo(ROOT).$("nt:base[xpathTestStatus=testing]").run();
+            results = plumber.newPipe(resolver).echo(ROOT).$("nt:base[xpathTestStatus=testing]").run().getCurrentPathSet();
             assertEquals("sling query should return as many items as we wrote", NB_ITEMS, results.size());
         }
     }
@@ -91,7 +91,7 @@ public class PipeBuilderIT extends PipesTestSupport {
             for (int i = 0; i < 10; i ++) {
                 plumber.newPipe(resolver).ref(pipe.getResource().getPath()).runWith("testedBinding", i);
             }
-            Set<String> results = plumber.newPipe(resolver).echo(ROOT).traverse().run();
+            Collection<String> results = plumber.newPipe(resolver).echo(ROOT).traverse().run().getCurrentPathSet();
             LOGGER.info("Following results are found {}", results);
             assertEquals("we should have root and implemented children", 11, results.size());
         }
diff --git a/src/test/java/org/apache/sling/pipes/it/PlumberServletIT.java b/src/test/java/org/apache/sling/pipes/it/PlumberServletIT.java
new file mode 100644
index 0000000..5ceb269
--- /dev/null
+++ b/src/test/java/org/apache/sling/pipes/it/PlumberServletIT.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.apache.sling.pipes.it;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.sling.pipes.internal.DefaultJsonWriter;
+import org.apache.sling.pipes.internal.JsonUtil;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.json.JsonObject;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.net.URL;
+import java.nio.charset.Charset;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class PlumberServletIT extends PipesTestSupport {
+    private static final Logger LOGGER = LoggerFactory.getLogger(PipeModelIT.class);
+
+    @Test
+    @Ignore
+    public void testListComponent() throws IOException {
+        final String urlString = String.format("http://localhost:%s/etc/pipes-it/another-list.json", httpPort());
+        LOGGER.info("fetching {}", urlString);
+        URL url = new URL(urlString);
+        StringWriter writer = new StringWriter();
+        IOUtils.copy(url.openStream(), writer, Charset.defaultCharset());
+        String response = writer.toString();
+        LOGGER.info("retrieved following response {}", response);
+        JsonObject main = JsonUtil.parseObject(response);
+        assertTrue("there should be an items key", main.containsKey(DefaultJsonWriter.KEY_ITEMS));
+        assertTrue("there should be a size key", main.containsKey(DefaultJsonWriter.KEY_SIZE));
+        assertEquals("there should be 2 elements", 2, main.getInt(DefaultJsonWriter.KEY_SIZE));
+    }
+}
+
diff --git a/src/test/resources/SLING-INF/jcr_root/content/fruits.json b/src/test/resources/SLING-INF/jcr_root/content/fruits.json
index eb53e89..1c1a96c 100644
--- a/src/test/resources/SLING-INF/jcr_root/content/fruits.json
+++ b/src/test/resources/SLING-INF/jcr_root/content/fruits.json
@@ -12,6 +12,7 @@
       "jcr:primaryType":"nt:unstructured",
       "pea":{
         "jcr:primaryType":"nt:unstructured",
+        "jcr:title":"Pea",
         "color":"green",
         "buttheyhavesamecolor":{
           "jcr:primaryType":"nt:unstructured"
@@ -23,6 +24,7 @@
       },
       "carrot":{
         "jcr:primaryType":"nt:unstructured",
+        "jcr:title":"Carrot",
         "andtheircolorisdifferent":{
           "jcr:primaryType":"nt:unstructured"
         }
diff --git a/src/test/resources/SLING-INF/jcr_root/etc/pipes-it/another-list.json b/src/test/resources/SLING-INF/jcr_root/etc/pipes-it/another-list.json
new file mode 100644
index 0000000..ecd1d28
--- /dev/null
+++ b/src/test/resources/SLING-INF/jcr_root/etc/pipes-it/another-list.json
@@ -0,0 +1,7 @@
+{
+  "jcr:primaryType":"nt:unstructured",
+  "jcr:description":"returns fruit list under /content/fruits",
+  "sling:resourceType":"slingPipes/children",
+  "path":"/content/fruits",
+  "expr":"nt:base"
+}
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
['"commits@sling.apache.org" <co...@sling.apache.org>'].