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 2018/09/04 15:37:33 UTC
[sling-org-apache-sling-pipes] branch master updated: SLING-7678
introduce gogo capability
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 d26b373 SLING-7678 introduce gogo capability
d26b373 is described below
commit d26b37383e422fb5a2621ea42a722bff3d2fae49
Author: Nicolas Peltier <pe...@gmail.com>
AuthorDate: Tue Sep 4 17:27:19 2018 +0200
SLING-7678 introduce gogo capability
- add run, build, execute, help gogo commands,
- use a PipeExecutor annotation to PipeBuilder API to centralize API commands in one place,
- extract command utilities to CommandUtil class,
- unit tests
---
src/main/java/org/apache/sling/pipes/BasePipe.java | 9 +-
.../java/org/apache/sling/pipes/OutputWriter.java | 11 +
src/main/java/org/apache/sling/pipes/Pipe.java | 3 +
.../java/org/apache/sling/pipes/PipeBindings.java | 4 +-
.../java/org/apache/sling/pipes/PipeBuilder.java | 71 ++++
.../java/org/apache/sling/pipes/PipeExecutor.java | 31 ++
src/main/java/org/apache/sling/pipes/Plumber.java | 52 ++-
.../java/org/apache/sling/pipes/SuperPipe.java | 2 +
.../sling/pipes/internal/AuthorizablePipe.java | 1 +
.../apache/sling/pipes/internal/CommandUtil.java | 47 +++
.../apache/sling/pipes/internal/GogoCommands.java | 457 +++++++++++++++++++++
.../apache/sling/pipes/internal/PackagePipe.java | 4 +-
.../sling/pipes/internal/PipeBuilderImpl.java | 29 +-
.../apache/sling/pipes/internal/PlumberImpl.java | 55 +--
.../apache/sling/pipes/internal/TraversePipe.java | 1 +
.../org/apache/sling/pipes/internal/WritePipe.java | 2 +
.../sling/pipes/internal/GogoCommandsTest.java | 122 ++++++
17 files changed, 827 insertions(+), 74 deletions(-)
diff --git a/src/main/java/org/apache/sling/pipes/BasePipe.java b/src/main/java/org/apache/sling/pipes/BasePipe.java
index 04ebff6..6e8230e 100644
--- a/src/main/java/org/apache/sling/pipes/BasePipe.java
+++ b/src/main/java/org/apache/sling/pipes/BasePipe.java
@@ -146,6 +146,7 @@ public class BasePipe implements Pipe {
/**
* Get pipe's expression, instanciated or not
* @return configured expression
+ * @throws ScriptException in case computed expression goes wrong
*/
public String getExpr() throws ScriptException {
return bindings.instantiateExpression(getRawExpression());
@@ -161,6 +162,7 @@ public class BasePipe implements Pipe {
/**
* Get pipe's path, instanciated or not
* @return configured path (can be empty)
+ * @throws ScriptException in case computed path goes wrong
*/
public String getPath() throws ScriptException {
String rawPath = getRawPath();
@@ -169,7 +171,7 @@ public class BasePipe implements Pipe {
/**
* @return computed path: getPath, with relative path taken in account
- * @throws ScriptException
+ * @throws ScriptException in case computed path goes wrong
*/
protected String getComputedPath() throws ScriptException {
String path = getPath();
@@ -282,9 +284,8 @@ public class BasePipe implements Pipe {
}
/**
- *
- * @return
- * @throws ScriptException
+ * @return outputs of the pipe, as an iterator of resources
+ * @throws ScriptException if any exception has occured
*/
protected Iterator<Resource> computeOutput() throws Exception {
Resource input = getInput();
diff --git a/src/main/java/org/apache/sling/pipes/OutputWriter.java b/src/main/java/org/apache/sling/pipes/OutputWriter.java
index ef28c3c..cd14d2a 100644
--- a/src/main/java/org/apache/sling/pipes/OutputWriter.java
+++ b/src/main/java/org/apache/sling/pipes/OutputWriter.java
@@ -98,6 +98,13 @@ public abstract class OutputWriter {
}
/**
+ * @param customOutputs custom outputs
+ */
+ public void setCustomOutputs(Map<String, Object> customOutputs) {
+ this.customOutputs = customOutputs;
+ }
+
+ /**
* Specifically init the response
* @param response response on which to write
*/
@@ -180,4 +187,8 @@ public abstract class OutputWriter {
public String toString() {
return writer.toString();
}
+
+ public Map<String, Object> getCustomOutputs() {
+ return customOutputs;
+ }
}
diff --git a/src/main/java/org/apache/sling/pipes/Pipe.java b/src/main/java/org/apache/sling/pipes/Pipe.java
index 416fa24..8a194dc 100644
--- a/src/main/java/org/apache/sling/pipes/Pipe.java
+++ b/src/main/java/org/apache/sling/pipes/Pipe.java
@@ -85,6 +85,7 @@ public interface Pipe {
* Get pipe current's resource *before* next execution, meaning either the
* configured resource, either previous' pipe output resource
* @return input, configured or previous pipe
+ * @throws ScriptException in case computed input goes wrong
*/
Resource getInput() throws ScriptException;
@@ -115,11 +116,13 @@ public interface Pipe {
/**
* to be executed before output is retrieved
+ * @throws Exception in case anything goes wrong
*/
void before() throws Exception;
/**
* to be executed before output is retrieved
+ * @throws Exception in case anything goes wrong
*/
void after() throws Exception;
diff --git a/src/main/java/org/apache/sling/pipes/PipeBindings.java b/src/main/java/org/apache/sling/pipes/PipeBindings.java
index eb5826a..34bfccd 100644
--- a/src/main/java/org/apache/sling/pipes/PipeBindings.java
+++ b/src/main/java/org/apache/sling/pipes/PipeBindings.java
@@ -277,7 +277,7 @@ public class PipeBindings {
* we implement here as a String
* @param expr ecma like expression
* @return String that is the result of the expression
- * @throws ScriptException
+ * @throws ScriptException in case expression computing went wrong
*/
public String instantiateExpression(String expr) throws ScriptException {
return (String)evaluate(expr);
@@ -287,7 +287,7 @@ public class PipeBindings {
* Instantiate object from expression
* @param expr ecma expression
* @return instantiated object
- * @throws ScriptException
+ * @throws ScriptException in case object computing went wrong
*/
public Object instantiateObject(String expr) throws ScriptException {
Object result = evaluate(expr);
diff --git a/src/main/java/org/apache/sling/pipes/PipeBuilder.java b/src/main/java/org/apache/sling/pipes/PipeBuilder.java
index 6e81756..41fdc32 100644
--- a/src/main/java/org/apache/sling/pipes/PipeBuilder.java
+++ b/src/main/java/org/apache/sling/pipes/PipeBuilder.java
@@ -18,6 +18,27 @@ package org.apache.sling.pipes;
import org.apache.sling.api.resource.PersistenceException;
import org.apache.sling.event.jobs.Job;
+import org.apache.sling.pipes.internal.AuthorizablePipe;
+import org.apache.sling.pipes.internal.FilterPipe;
+import org.apache.sling.pipes.internal.MovePipe;
+import org.apache.sling.pipes.internal.MultiPropertyPipe;
+import org.apache.sling.pipes.internal.NotPipe;
+import org.apache.sling.pipes.internal.PackagePipe;
+import org.apache.sling.pipes.internal.PathPipe;
+import org.apache.sling.pipes.internal.ReferencePipe;
+import org.apache.sling.pipes.internal.RemovePipe;
+import org.apache.sling.pipes.internal.TraversePipe;
+import org.apache.sling.pipes.internal.WritePipe;
+import org.apache.sling.pipes.internal.XPathPipe;
+import org.apache.sling.pipes.internal.inputstream.CsvPipe;
+import org.apache.sling.pipes.internal.inputstream.JsonPipe;
+import org.apache.sling.pipes.internal.inputstream.RegexpPipe;
+import org.apache.sling.pipes.internal.slingquery.ChildrenPipe;
+import org.apache.sling.pipes.internal.slingquery.ClosestPipe;
+import org.apache.sling.pipes.internal.slingquery.FindPipe;
+import org.apache.sling.pipes.internal.slingquery.ParentPipe;
+import org.apache.sling.pipes.internal.slingquery.ParentsPipe;
+import org.apache.sling.pipes.internal.slingquery.SiblingsPipe;
import org.osgi.annotation.versioning.ProviderType;
import java.util.Map;
@@ -39,6 +60,8 @@ public interface PipeBuilder {
* @param expr target of the resource to move
* @return updated instance of PipeBuilder
*/
+ @PipeExecutor(command = "mv", resourceType = MovePipe.RESOURCE_TYPE, pipeClass = MovePipe.class,
+ description = "move current resource to expr (more on https://sling.apache.org/documentation/bundles/sling-pipes/writers.html)")
PipeBuilder mv(String expr);
/**
@@ -47,6 +70,8 @@ public interface PipeBuilder {
* @return updated instance of PipeBuilder
* @throws IllegalAccessException in case it's called with bad configuration
*/
+ @PipeExecutor(command = "write", resourceType = WritePipe.RESOURCE_TYPE, pipeClass = WritePipe.class,
+ description = "write following key=value pairs to the current resource")
PipeBuilder write(Object... conf) throws IllegalAccessException;
/**
@@ -55,6 +80,8 @@ public interface PipeBuilder {
* @return updated instance of PipeBuilder
* @throws IllegalAccessException in case it's called with bad configuration
*/
+ @PipeExecutor(command = "grep", resourceType = FilterPipe.RESOURCE_TYPE, pipeClass = FilterPipe.class,
+ description = "filter current resources with following key=value pairs")
PipeBuilder grep(Object... conf) throws IllegalAccessException;
/**
@@ -63,6 +90,8 @@ public interface PipeBuilder {
* @return updated instance of PipeBuilder
* @throws IllegalAccessException in case it's called with bad configuration
*/
+ @PipeExecutor(command = "auth", resourceType = AuthorizablePipe.RESOURCE_TYPE, pipeClass = AuthorizablePipe.class,
+ description = "convert current resource as authorizable")
PipeBuilder auth(Object... conf) throws IllegalAccessException;
/**
@@ -70,6 +99,8 @@ public interface PipeBuilder {
* @param expr xpath expression
* @return updated instance of PipeBuilder
*/
+ @PipeExecutor(command = "xpath", resourceType = XPathPipe.RESOURCE_TYPE, pipeClass = XPathPipe.class,
+ description = "create following xpath query's result as output resources")
PipeBuilder xpath(String expr);
/**
@@ -77,6 +108,8 @@ public interface PipeBuilder {
* @param expr sling query expression
* @return updated instance of PipeBuilder
*/
+ @PipeExecutor(command = "children", resourceType = ChildrenPipe.RESOURCE_TYPE, pipeClass = ChildrenPipe.class,
+ description = "list current resource's immediate children")
PipeBuilder children(String expr);
/**
@@ -84,12 +117,16 @@ public interface PipeBuilder {
* @param expr sling query expression
* @return updated instance of PipeBuilder
*/
+ @PipeExecutor(command = "siblings", resourceType = SiblingsPipe.RESOURCE_TYPE, pipeClass = SiblingsPipe.class,
+ description = "list current resource's siblings")
PipeBuilder siblings(String expr);
/**
* attach a rm pipe to the current context
* @return updated instance of PipeBuilder
*/
+ @PipeExecutor(command = "rm", resourceType = RemovePipe.RESOURCE_TYPE, pipeClass = RemovePipe.class,
+ description = "remove current resource")
PipeBuilder rm();
/**
@@ -97,6 +134,8 @@ public interface PipeBuilder {
* @param expr csv expr or URL or path in the resource tree
* @return updated instance of PipeBuilder
*/
+ @PipeExecutor(command = "csv", resourceType = CsvPipe.RESOURCE_TYPE, pipeClass = CsvPipe.class,
+ description = "read expr's csv and output each line in the bindings")
PipeBuilder csv(String expr);
/**
@@ -104,6 +143,8 @@ public interface PipeBuilder {
* @param expr json expr or URL or path in the resource tree
* @return updated instance of PipeBuilder
*/
+ @PipeExecutor(command = "json", resourceType = JsonPipe.RESOURCE_TYPE, pipeClass = JsonPipe.class,
+ description = "read expr's json array and output each object in the bindings")
PipeBuilder json(String expr);
/**
@@ -111,6 +152,8 @@ public interface PipeBuilder {
* @param expr text expr or URL or path in the resource tree
* @return updated instance of PipeBuilder
*/
+ @PipeExecutor(command = "egrep", resourceType = RegexpPipe.RESOURCE_TYPE, pipeClass = RegexpPipe.class,
+ description = "read expr's txt and output each found pattern in the binding")
PipeBuilder egrep(String expr);
/**
@@ -118,6 +161,8 @@ public interface PipeBuilder {
* @param expr path to create
* @return updated instance of PipeBuilder
*/
+ @PipeExecutor(command = "mkdir", resourceType = PathPipe.RESOURCE_TYPE, pipeClass = PathPipe.class,
+ description = "create expr path")
PipeBuilder mkdir(String expr);
/**
@@ -125,18 +170,24 @@ public interface PipeBuilder {
* @param path pipe path
* @return updated instance of PipeBuilder
*/
+ @PipeExecutor(command = "echo", resourceType = BasePipe.RESOURCE_TYPE, pipeClass = BasePipe.class,
+ description = "output input's path")
PipeBuilder echo(String path);
/**
* attach a traverse pipe to the current context
* @return updated instance of PipeBuilder
*/
+ @PipeExecutor(command = "traverse", resourceType = TraversePipe.RESOURCE_TYPE, pipeClass = TraversePipe.class,
+ description = "traverse current resource")
PipeBuilder traverse();
/**
* attach a sling query parent pipe to the current context
* @return updated instance of PipeBuilder
*/
+ @PipeExecutor(command = "parent", resourceType = ParentPipe.RESOURCE_TYPE, pipeClass = ParentPipe.class,
+ description = "return current's resource parent")
PipeBuilder parent();
/**
@@ -144,6 +195,8 @@ public interface PipeBuilder {
* @param expr expression
* @return updated instance of PipeBuilder
*/
+ @PipeExecutor(command = "parents", resourceType = ParentsPipe.RESOURCE_TYPE, pipeClass = ParentsPipe.class,
+ description = "return current's resource parents")
PipeBuilder parents(String expr);
/**
@@ -151,6 +204,8 @@ public interface PipeBuilder {
* @param expr expression
* @return updated instance of PipeBuilder
*/
+ @PipeExecutor(command = "closest", resourceType = ClosestPipe.RESOURCE_TYPE, pipeClass = ClosestPipe.class,
+ description = "return closest resource of the current")
PipeBuilder closest(String expr);
/**
@@ -158,6 +213,8 @@ public interface PipeBuilder {
* @param expr expression
* @return updated instance of PipeBuilder
*/
+ @PipeExecutor(command = "$", resourceType = FindPipe.RESOURCE_TYPE, pipeClass = FindPipe.class,
+ description = "find resource from the current, with the given expression as a parameter")
PipeBuilder $(String expr);
/**
@@ -165,6 +222,8 @@ public interface PipeBuilder {
* @param expr reference
* @return updated instance of PipeBuilder
*/
+ @PipeExecutor(command = "ref", resourceType = ReferencePipe.RESOURCE_TYPE, pipeClass = ReferencePipe.class,
+ description = "reference passed pipe")
PipeBuilder ref(String expr);
/**
@@ -172,6 +231,8 @@ public interface PipeBuilder {
* @param expr path of the pipe
* @return updated instance of PipeBuilder
*/
+ @PipeExecutor(command = "pkg", resourceType = PackagePipe.RESOURCE_TYPE, pipeClass = PackagePipe.class,
+ description = "package up current resource in given package")
PipeBuilder pkg(String expr);
/**
@@ -179,9 +240,19 @@ public interface PipeBuilder {
* @param expr reference
* @return updated instance of PipeBuilder
*/
+ @PipeExecutor(command = "not", resourceType = NotPipe.RESOURCE_TYPE, pipeClass = NotPipe.class,
+ description = "invert output: if input, return nothing, if no input, return single resource")
PipeBuilder not(String expr);
/**
+ * attach a multi value property pipe to the current context
+ * @return updated instance of PipeBuilder
+ */
+ @PipeExecutor(command = "mp", resourceType = MultiPropertyPipe.RESOURCE_TYPE, pipeClass = MultiPropertyPipe.class,
+ description = "read multi property, and output each value in the bindings")
+ PipeBuilder mp();
+
+ /**
* parameterized current pipe in the context
* @param params key value pair of parameters
* @return updated instance of PipeBuilder
diff --git a/src/main/java/org/apache/sling/pipes/PipeExecutor.java b/src/main/java/org/apache/sling/pipes/PipeExecutor.java
new file mode 100644
index 0000000..105b3b6
--- /dev/null
+++ b/src/main/java/org/apache/sling/pipes/PipeExecutor.java
@@ -0,0 +1,31 @@
+/*
+ * 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 java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface PipeExecutor {
+ String command();
+ String resourceType();
+ String description();
+ Class pipeClass();
+}
diff --git a/src/main/java/org/apache/sling/pipes/Plumber.java b/src/main/java/org/apache/sling/pipes/Plumber.java
index 421bf66..53f695d 100644
--- a/src/main/java/org/apache/sling/pipes/Plumber.java
+++ b/src/main/java/org/apache/sling/pipes/Plumber.java
@@ -33,7 +33,9 @@ public interface Plumber {
/**
* Instantiate a pipe from the given resource and returns it
+ *
* @param resource configuration resource
+ *
* @return pipe instantiated from the resource, null otherwise
*/
Pipe getPipe(Resource resource);
@@ -41,8 +43,10 @@ public interface Plumber {
/**
* Instantiate a pipe from the given resource and returns it
- * @param resource configuration resource
+ *
+ * @param resource configuration resource
* @param upperBindings already set binding we want to initiate our pipe with
+ *
* @return pipe instantiated from the resource, null otherwise
*/
Pipe getPipe(Resource resource, PipeBindings upperBindings);
@@ -50,48 +54,59 @@ public interface Plumber {
/**
* executes in a background thread
+ *
* @param resolver resolver used for registering the execution (id will be checked against the configuration)
- * @param path path of the pipe to execute
+ * @param path path of the pipe to execute
* @param bindings additional bindings to use when executing
+ *
* @return Job if registered, null otherwise
*/
Job executeAsync(ResourceResolver resolver, String path, Map bindings);
/**
* executes in a background thread
- * @param path path of the pipe to execute
+ *
+ * @param path path of the pipe to execute
* @param bindings additional bindings to use when executing
+ *
* @return Job if registered, null otherwise
*/
Job executeAsync(String path, Map bindings);
/**
* Executes a pipe at a certain path
+ *
* @param resolver resource resolver with which pipe will be executed
- * @param path path of a valid pipe configuration
+ * @param path path of a valid pipe configuration
* @param bindings bindings to add to the execution of the pipe, can be null
- * @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
+ * @param writer output of the pipe
+ * @param save in case that pipe writes anything, wether the plumber should save changes or not
+ *
* @return instance of <code>ExecutionResult</code>
+ *
+ * @throws Exception in case execution fails
*/
ExecutionResult execute(ResourceResolver resolver, String path, Map bindings, OutputWriter writer, boolean save) throws Exception;
/**
* Executes a given pipe
+ *
* @param resolver resource resolver with which pipe will be executed
- * @param pipe pipe to execute
+ * @param pipe pipe to execute
* @param bindings bindings to add to the execution of the pipe, can be null
- * @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
+ * @param writer output of the pipe
+ * @param save in case that pipe writes anything, wether the plumber should save changes or not
+ *
* @return instance of <code>ExecutionResult</code>
+ *
+ * @throws Exception in case execution fails
*/
ExecutionResult execute(ResourceResolver resolver, Pipe pipe, Map bindings, OutputWriter writer, boolean save) throws Exception;
/**
* Registers
- * @param type resource type of the pipe to register
+ *
+ * @param type resource type of the pipe to register
* @param pipeClass class of the pipe to register
*/
void registerPipe(String type, Class<? extends BasePipe> pipeClass);
@@ -99,29 +114,42 @@ public interface Plumber {
/**
* returns wether or not a pipe type is registered
+ *
* @param type resource type tested
+ *
* @return true if the type is registered, false if not
*/
boolean isTypeRegistered(String type);
/**
* status of the pipe
+ *
* @param pipeResource resource corresponding to the pipe
+ *
* @return status of the pipe, can be blank, 'started' or 'finished'
*/
String getStatus(Resource pipeResource);
/**
* Provides a builder helping quickly build and execute a pipe
+ *
* @param resolver resource resolver that will be used for building the pipe
+ *
* @return instance of PipeBuilder
*/
PipeBuilder newPipe(ResourceResolver resolver);
/**
* returns true if the pipe is considered to be running
+ *
* @param pipeResource resource corresponding to the pipe
+ *
* @return true if still running
*/
boolean isRunning(Resource pipeResource);
+
+ /**
+ * @return service user that has been configured for executing pipes;
+ */
+ Map getServiceUser();
}
diff --git a/src/main/java/org/apache/sling/pipes/SuperPipe.java b/src/main/java/org/apache/sling/pipes/SuperPipe.java
index e83260b..022fbcf 100644
--- a/src/main/java/org/apache/sling/pipes/SuperPipe.java
+++ b/src/main/java/org/apache/sling/pipes/SuperPipe.java
@@ -55,11 +55,13 @@ public abstract class SuperPipe extends BasePipe {
/**
* build the subpipes pipes list
+ * @throws Exception in case one of the child building has went wrong
*/
public abstract void buildChildren() throws Exception;
/**
* @return output of this super pipe's subpipes
+ * @throws Exception in case one of the outputs computation went wrong
*/
protected abstract Iterator<Resource> computeSubpipesOutput() throws Exception;
diff --git a/src/main/java/org/apache/sling/pipes/internal/AuthorizablePipe.java b/src/main/java/org/apache/sling/pipes/internal/AuthorizablePipe.java
index 2522223..0fd4779 100644
--- a/src/main/java/org/apache/sling/pipes/internal/AuthorizablePipe.java
+++ b/src/main/java/org/apache/sling/pipes/internal/AuthorizablePipe.java
@@ -71,6 +71,7 @@ public class AuthorizablePipe extends BasePipe {
* public constructor
* @param plumber plumber instance
* @param resource configuration resource
+ * @param upperBindings bindings coming from super pipe
* @throws Exception bad configuration handling
*/
public AuthorizablePipe(Plumber plumber, Resource resource, PipeBindings upperBindings) throws Exception {
diff --git a/src/main/java/org/apache/sling/pipes/internal/CommandUtil.java b/src/main/java/org/apache/sling/pipes/internal/CommandUtil.java
new file mode 100644
index 0000000..03fbfc1
--- /dev/null
+++ b/src/main/java/org/apache/sling/pipes/internal/CommandUtil.java
@@ -0,0 +1,47 @@
+/*
+ * 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.Map;
+
+/**
+ * utilities for user input
+ */
+public class CommandUtil {
+ /**
+ * Checks arguments and throws exception if there is an issue
+ * @param params arguments to check
+ * @throws IllegalArgumentException exception thrown in case arguments are wrong
+ */
+ public static void checkArguments(Object... params) throws IllegalArgumentException {
+ if (params.length % 2 > 0){
+ throw new IllegalArgumentException("there should be an even number of arguments");
+ }
+ }
+
+ /**
+ * write key/value pairs into a map
+ * @param map target map
+ * @param params key/value pairs to write into the map
+ */
+ public static void writeToMap(Map map, Object... params){
+ for (int i = 0; i < params.length - 1; i += 2){
+ map.put(params[i], params[i + 1]);
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/pipes/internal/GogoCommands.java b/src/main/java/org/apache/sling/pipes/internal/GogoCommands.java
new file mode 100644
index 0000000..7c225c2
--- /dev/null
+++ b/src/main/java/org/apache/sling/pipes/internal/GogoCommands.java
@@ -0,0 +1,457 @@
+/*
+ * 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 org.apache.commons.lang3.StringUtils;
+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.pipes.ExecutionResult;
+import org.apache.sling.pipes.OutputWriter;
+import org.apache.sling.pipes.PipeBuilder;
+import org.apache.sling.pipes.PipeExecutor;
+import org.apache.sling.pipes.Plumber;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.sling.pipes.internal.CommandUtil.writeToMap;
+
+@Component(immediate = true,
+ service = GogoCommands.class,
+ property = {
+ "osgi.command.scope=pipe",
+ "osgi.command.function=build",
+ "osgi.command.function=run",
+ "osgi.command.function=execute",
+ "osgi.command.function=help"
+ })
+
+public class GogoCommands {
+ final Logger log = LoggerFactory.getLogger(GogoCommands.class);
+
+ protected final static String SEPARATOR = "/";
+ protected final static String PARAMS = "@";
+ protected final static String KEY_VALUE_SEP = "=";
+ protected final static String KEY_NAME = "name";
+ protected final static String KEY_PATH = "path";
+ protected final static String KEY_EXPR = "expr";
+
+ @Reference
+ ResourceResolverFactory factory;
+
+ @Reference
+ Plumber plumber;
+
+ Map<String, Method> methodMap;
+
+ Map<String, PipeExecutor> executorMap;
+
+ /**
+ * run command handler
+ * @param cmds string tokens coming with run command
+ * @throws Exception in case anything went wrong
+ */
+ public void run(String... cmds) throws Exception {
+ try (ResourceResolver resolver = factory.getServiceResourceResolver(plumber.getServiceUser())) {
+ PipeBuilder builder = parse(resolver, cmds);
+ System.out.println(builder.run());
+ }
+ }
+
+ /**
+ * build command handler
+ * @param cmds string tokens coming with build command
+ * @throws Exception in case anything went wrong
+ */
+ public void build(String... cmds) throws Exception {
+ try (ResourceResolver resolver = factory.getServiceResourceResolver(plumber.getServiceUser())) {
+ PipeBuilder builder = parse(resolver, cmds);
+ System.out.println(builder.build().getResource().getPath());
+ }
+ }
+
+ /**
+ * execute command handler
+ * @param path pipe path
+ * @param options string tokens coming with run command
+ * @throws Exception in case anything went wrong
+ */
+ public void execute(String path, String... options) throws Exception {
+ try (ResourceResolver resolver = factory.getServiceResourceResolver(plumber.getServiceUser())) {
+ System.out.println(executeInternal(resolver, path, options));
+ } catch(Exception e){
+ log.error("Unable to execute {}", path, e);
+ throw(e);
+ }
+ }
+
+ /**
+ * internal execution command handler
+ * @param resolver
+ * @param path
+ * @param optionTokens
+ * @return Execution results
+ * @throws Exception
+ */
+ protected ExecutionResult executeInternal(ResourceResolver resolver, String path, String... optionTokens) throws Exception {
+ Resource resource = resolver.getResource(path);
+ if (resource == null){
+ throw new IllegalArgumentException(String.format("%s resource does not exist", path));
+ }
+ Options options = getOptions(optionTokens);
+ Map bMap = null;
+ if (options.bindings != null) {
+ bMap = new HashMap();
+ writeToMap(bMap, options.bindings);
+ }
+ OutputWriter writer = new NopWriter();
+ if (options.writer != null){
+ writer = options.writer;
+ }
+ writer.starts();
+ return plumber.execute(resolver, path, bMap, writer, true);
+ }
+
+ /**
+ * help command handler
+ */
+ public void help(){
+ System.out.format("Available commands are \n- execute <path> <options>(execute a pipe already built at a given path)" +
+ "\n- build (build pipe as configured in arguments)" +
+ "\n- run (run pipe as configured in arguments)" +
+ "\n- help (print this help)" +
+ "\n\nfor pipe configured in argument, do 'pipe:<run|build|runAsync> <pipe token> (/ <pipe token> )*\n" +
+ "\n a <pipe token> is <pipe> <expr|conf>? (<options>)?" +
+ "\n <options> are (@ <option>)* form with <option> being either" +
+ "\n\t'name pipeName' (used in bindings), " +
+ "\n\t'expr pipeExpression' (when not directly as <args>)" +
+ "\n\t'path pipePath' (when not directly as <args>)" +
+ "\n\t'bindings key=value ...'" +
+ "\n\t'outputs key=value ...'" +
+ "\n and <pipe> is one of the following :\n");
+ for (Map.Entry<String, PipeExecutor> entry : getExecutorMap().entrySet()){
+ System.out.format("\t%s : %s\n", entry.getKey(), entry.getValue().description() );
+ }
+ }
+
+ /**
+ * @param resolver resource resolver with which pipe will build the pipe
+ * @param cmds list of commands for building the pipe
+ * @return PipeBuilder instance (that can be used to finalize the command)
+ * @throws InvocationTargetException can happen in case the mapping with PB api went wrong
+ * @throws IllegalAccessException can happen in case the mapping with PB api went wrong
+ */
+ protected PipeBuilder parse(ResourceResolver resolver, String...cmds) throws InvocationTargetException, IllegalAccessException {
+ PipeBuilder builder = plumber.newPipe(resolver);
+ for (Token token : parseTokens(cmds)){
+ Method method = getMethodMap().get(token.pipeKey);
+ if (method == null){
+ throw new IllegalArgumentException(token.pipeKey + " is not a valid pipe");
+ }
+ if (isExpressionExpected(method)){
+ method.invoke(builder, token.args.get(0));
+ } else if (isConfExpected(method)){
+ method.invoke(builder, (Object)keyValuesToArray(token.args));
+ } else if (isWithoutExpectedParameter(method)){
+ method.invoke(builder);
+ }
+
+ if (token.options != null){
+ token.options.writeToBuilder(builder);
+ }
+ }
+ return builder;
+
+ }
+
+ /**
+ * builds utility maps
+ */
+ protected void computeMaps(){
+ executorMap = new HashMap<>();
+ methodMap = new HashMap<>();
+ for (Method method : PipeBuilder.class.getDeclaredMethods()) {
+ PipeExecutor executor = method.getAnnotation(PipeExecutor.class);
+ if (executor != null) {
+ methodMap.put(executor.command(), method);
+ executorMap.put(executor.command(), executor);
+ }
+ }
+ }
+
+ /**
+ * @return map of command to PB api method
+ */
+ protected Map<String, Method> getMethodMap() {
+ if (methodMap == null) {
+ computeMaps();
+ }
+ return methodMap;
+ }
+
+ /**
+ * @return map of command to Annotation information around the PB api
+ */
+ protected Map<String, PipeExecutor> getExecutorMap() {
+ if (executorMap == null) {
+ computeMaps();
+ }
+ return executorMap;
+ }
+
+ /**
+ * @param method corresponding PB api
+ * @return true if the api does expect an expression (meaning a string)
+ */
+ protected boolean isExpressionExpected(Method method) {
+ return method.getParameterCount() == 1 && method.getParameterTypes()[0].equals(String.class);
+ }
+
+ /**
+ * @param method corresponding PB api
+ * @return true if the api does expect a configuration (meaning a list of key value pairs)
+ */
+ protected boolean isConfExpected(Method method) {
+ return method.getParameterCount() == 1 && method.getParameterTypes()[0].equals(Object[].class);
+ }
+
+ /**
+ * @param method corresponding PB api
+ * @return true if the api does not expect parameters
+ */
+ protected boolean isWithoutExpectedParameter(Method method){
+ return method.getParameterCount() == 0;
+ }
+
+ /**
+ * @param o list of key value strings key1:value1,key2:value2,...
+ * @return String [] key1,value1,key2,value2,... corresponding to the pipe builder API
+ */
+ private String[] keyValuesToArray(List<String> o) {
+ List<String> args = new ArrayList<>();
+ for (String pair : o){
+ args.addAll(Arrays.asList(pair.split(KEY_VALUE_SEP)));
+ }
+ return args.toArray(new String[args.size()]);
+ }
+
+
+ /**
+ * @param commands full list of command tokens
+ * @return Token list corresponding to the string ones
+ */
+ protected List<Token> parseTokens(String... commands) {
+ List<Token> returnValue = new ArrayList();
+ Token currentToken = new Token();
+ returnValue.add(currentToken);
+ List currentList = new ArrayList();
+ for (String token : commands){
+ if (currentToken.pipeKey == null){
+ currentToken.pipeKey = token;
+ } else {
+ switch (token){
+ case GogoCommands.SEPARATOR:
+ finishToken(currentToken, currentList);
+ currentList = new ArrayList();
+ currentToken = new Token();
+ returnValue.add(currentToken);
+ break;
+ case GogoCommands.PARAMS:
+ currentToken.args = currentList;
+ currentList = new ArrayList();
+ currentList.add(PARAMS);
+ break;
+ default:
+ currentList.add(token);
+ }
+ }
+ }
+ finishToken(currentToken, currentList);
+ return returnValue;
+ }
+
+ /**
+ * ends up processing of current token
+ * @param currentToken token being processed
+ * @param currentList list of argument that have been collected so far
+ */
+ protected void finishToken(Token currentToken, List<String> currentList){
+ if (currentToken.args != null){
+ //it means we have already parse args here, so we need to set current list as options
+ currentToken.options = getOptions(currentList);
+ } else {
+ currentToken.args = currentList;
+ }
+ log.debug("current token : {}", currentToken);
+ }
+
+ /**
+ * Pipe token, used to hold information of a "sub pipe" configuration
+ */
+ protected class Token {
+ String pipeKey;
+ List args;
+ Options options;
+
+ @Override
+ public String toString() {
+ return "Token{" +
+ "pipeKey='" + pipeKey + '\'' +
+ ", args=" + args +
+ ", options=" + options +
+ '}';
+ }
+ }
+
+ /**
+ * @param tokens array of tokens
+ * @return options from array
+ */
+ protected Options getOptions(String[] tokens){
+ return getOptions(Arrays.asList(tokens));
+ }
+
+ /**
+ * @param tokens list of toekns
+ * @return options from token list
+ */
+ protected Options getOptions(List<String> tokens){
+ return new Options(tokens);
+ }
+
+ /**
+ * Options for a pipe execution
+ */
+ protected class Options {
+ String name;
+ String path;
+ String expr;
+ String[] bindings;
+ OutputWriter writer;
+
+ @Override
+ public String toString() {
+ return "Options{" +
+ "name='" + name + '\'' +
+ ", path='" + path + '\'' +
+ ", expr='" + expr + '\'' +
+ ", bindings=" + Arrays.toString(bindings) +
+ ", writer=" + writer +
+ '}';
+ }
+
+ /**
+ * Constructor
+ * @param options string list from where options will be built
+ */
+ protected Options(List<String> options){
+ Map<String, Object> optionMap = new HashMap<>();
+ String currentKey = null;
+ List<String> currentList = null;
+ for (String optionToken : options) {
+ if (PARAMS.equals(optionToken)){
+ finishOption(currentKey, currentList, optionMap);
+ currentList = new ArrayList<>();
+ currentKey = null;
+ } else if (currentKey == null){
+ currentKey = optionToken;
+ } else {
+ currentList.add(optionToken);
+ }
+ }
+ finishOption(currentKey, currentList, optionMap);
+ for (Map.Entry<String, Object> entry : optionMap.entrySet()){
+ switch (entry.getKey()) {
+ case "name" : {
+ this.name = (String)entry.getValue();
+ break;
+ }
+ case "path" : {
+ this.path = (String)entry.getValue();
+ break;
+ }
+ case "expr" : {
+ this.expr = (String)entry.getValue();
+ break;
+ }
+ case "with" : {
+ this.bindings = keyValuesToArray((List<String>)entry.getValue());
+ break;
+ }
+ case "outputs" : {
+ this.writer = new JsonWriter();
+ String[] list = keyValuesToArray((List<String>)entry.getValue());
+ Map outputs = new HashMap();
+ CommandUtil.writeToMap(outputs, list);
+ this.writer.setCustomOutputs(outputs);
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException(String.format("%s is an unknown option", entry.getKey()));
+ }
+ }
+ }
+
+ }
+
+ /**
+ * wrap up current option
+ * @param currentKey option key
+ * @param currentList list being processed
+ * @param optionMap option map
+ */
+ protected void finishOption(String currentKey, List<String> currentList, Map<String, Object> optionMap){
+ if (currentList != null){
+ if (currentKey.equals(KEY_NAME) || currentKey.equals(KEY_EXPR) || currentKey.equals(KEY_PATH)) {
+ optionMap.put(currentKey, currentList.get(0));
+ } else {
+ optionMap.put(currentKey, currentList);
+ }
+ }
+ }
+
+ /**
+ * write options to current builder
+ * @param builder current builder
+ * @throws IllegalAccessException
+ */
+ void writeToBuilder(PipeBuilder builder) throws IllegalAccessException {
+ if (StringUtils.isNotBlank(name)){
+ builder.name(name);
+ }
+ if (StringUtils.isNotBlank(path)){
+ builder.path(path);
+ }
+ if (StringUtils.isNotBlank(expr)){
+ builder.expr(expr);
+ }
+ if (bindings != null){
+ builder.with(bindings);
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/sling/pipes/internal/PackagePipe.java b/src/main/java/org/apache/sling/pipes/internal/PackagePipe.java
index 73e46cb..ff2ae5b 100644
--- a/src/main/java/org/apache/sling/pipes/internal/PackagePipe.java
+++ b/src/main/java/org/apache/sling/pipes/internal/PackagePipe.java
@@ -65,7 +65,7 @@ public class PackagePipe extends BasePipe {
*
* @param plumber plumber
* @param resource configuration resource
- *
+ * @param upperBindings super pipe's bindings
* @throws Exception in case configuration is not working
*/
public PackagePipe(Plumber plumber, Resource resource, PipeBindings upperBindings) throws Exception {
@@ -105,6 +105,8 @@ public class PackagePipe extends BasePipe {
* computes configured package based on expression configuration (either existing or creating it)
* @throws IOException problem with binary
* @throws RepositoryException problem with package persistence
+ * @throws IOException problem with package build
+ * @throws ScriptException problem with some expression/path compute
*/
protected void init() throws IOException, RepositoryException, ScriptException {
if (jcrPackage == null){
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 775e55c..cc825e7 100644
--- a/src/main/java/org/apache/sling/pipes/internal/PipeBuilderImpl.java
+++ b/src/main/java/org/apache/sling/pipes/internal/PipeBuilderImpl.java
@@ -51,6 +51,8 @@ import static org.apache.sling.jcr.resource.JcrResourceConstants.NT_SLING_FOLDER
import static org.apache.sling.jcr.resource.JcrResourceConstants.NT_SLING_ORDERED_FOLDER;
import static org.apache.sling.jcr.resource.JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY;
+import static org.apache.sling.pipes.internal.CommandUtil.checkArguments;
+import static org.apache.sling.pipes.internal.CommandUtil.writeToMap;
/**
* Implementation of the PipeBuilder interface
*/
@@ -213,6 +215,11 @@ public class PipeBuilderImpl implements PipeBuilder {
}
@Override
+ public PipeBuilder mp() {
+ return pipe(MultiPropertyPipe.RESOURCE_TYPE);
+ }
+
+ @Override
public PipeBuilder pkg(String expr) {
try {
pipeWithExpr(PackagePipe.RESOURCE_TYPE, expr).with(PackagePipe.PN_FILTERCOLLECTIONMODE, true);
@@ -238,28 +245,6 @@ public class PipeBuilderImpl implements PipeBuilder {
}
/**
- * Checks arguments and throws exception if there is an issue
- * @param params arguments to check
- * @throws IllegalArgumentException exception thrown in case arguments are wrong
- */
- protected void checkArguments(Object... params) throws IllegalArgumentException {
- if (params.length % 2 > 0){
- throw new IllegalArgumentException("there should be an even number of arguments");
- }
- }
-
- /**
- * write key/value pairs into a map
- * @param map target map
- * @param params key/value pairs to write into the map
- */
- protected void writeToMap(Map map, Object... params){
- for (int i = 0; i < params.length; i += 2){
- map.put(params[i], params[i + 1]);
- }
- }
-
- /**
* Add some configurations to current's Step node defined by name (if null, will be step's properties)
* @param name name of the configuration node, can be null in which case it's the subpipe itself
* @param params key/value pair list of configuration
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 6f84674..470527e 100644
--- a/src/main/java/org/apache/sling/pipes/internal/PlumberImpl.java
+++ b/src/main/java/org/apache/sling/pipes/internal/PlumberImpl.java
@@ -39,17 +39,9 @@ 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.PipeExecutor;
import org.apache.sling.pipes.Plumber;
import org.apache.sling.pipes.PlumberMXBean;
-import org.apache.sling.pipes.internal.inputstream.CsvPipe;
-import org.apache.sling.pipes.internal.inputstream.JsonPipe;
-import org.apache.sling.pipes.internal.inputstream.RegexpPipe;
-import org.apache.sling.pipes.internal.slingquery.ChildrenPipe;
-import org.apache.sling.pipes.internal.slingquery.ClosestPipe;
-import org.apache.sling.pipes.internal.slingquery.FindPipe;
-import org.apache.sling.pipes.internal.slingquery.ParentPipe;
-import org.apache.sling.pipes.internal.slingquery.ParentsPipe;
-import org.apache.sling.pipes.internal.slingquery.SiblingsPipe;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
@@ -67,6 +59,7 @@ import javax.jcr.query.Query;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -134,33 +127,29 @@ public class PlumberImpl implements Plumber, JobConsumer, PlumberMXBean {
serviceUser = configuration.serviceUser() != null ? Collections.singletonMap(SUBSERVICE, configuration.serviceUser()) : null;
allowedUsers = Arrays.asList(configuration.authorizedUsers());
registry = new HashMap<>();
- registerPipe(BasePipe.RESOURCE_TYPE, BasePipe.class);
- registerPipe(ContainerPipe.RESOURCE_TYPE, ContainerPipe.class);
- registerPipe(ChildrenPipe.RESOURCE_TYPE, ChildrenPipe.class);
- registerPipe(WritePipe.RESOURCE_TYPE, WritePipe.class);
- registerPipe(JsonPipe.RESOURCE_TYPE, JsonPipe.class);
- registerPipe(MultiPropertyPipe.RESOURCE_TYPE, MultiPropertyPipe.class);
- registerPipe(AuthorizablePipe.RESOURCE_TYPE, AuthorizablePipe.class);
- registerPipe(XPathPipe.RESOURCE_TYPE, XPathPipe.class);
- registerPipe(ReferencePipe.RESOURCE_TYPE, ReferencePipe.class);
- registerPipe(RemovePipe.RESOURCE_TYPE, RemovePipe.class);
- registerPipe(ParentsPipe.RESOURCE_TYPE, ParentsPipe.class);
- registerPipe(MovePipe.RESOURCE_TYPE, MovePipe.class);
- registerPipe(PathPipe.RESOURCE_TYPE, PathPipe.class);
- registerPipe(FilterPipe.RESOURCE_TYPE, FilterPipe.class);
- registerPipe(NotPipe.RESOURCE_TYPE, NotPipe.class);
- registerPipe(TraversePipe.RESOURCE_TYPE, TraversePipe.class);
- registerPipe(CsvPipe.RESOURCE_TYPE, CsvPipe.class);
- registerPipe(ParentPipe.RESOURCE_TYPE, ParentPipe.class);
- registerPipe(SiblingsPipe.RESOURCE_TYPE, SiblingsPipe.class);
- registerPipe(ClosestPipe.RESOURCE_TYPE, ClosestPipe.class);
- registerPipe(FindPipe.RESOURCE_TYPE, FindPipe.class);
- registerPipe(RegexpPipe.RESOURCE_TYPE, RegexpPipe.class);
- registerPipe(PackagePipe.RESOURCE_TYPE, PackagePipe.class);
+ registerPipes();
toggleJmxRegistration(this, PlumberMXBean.class.getName(), true);
refreshMonitoredPipes();
}
+ /**
+ * Register all pipes declared in pipe builder
+ */
+ protected void registerPipes(){
+ registerPipe(ContainerPipe.RESOURCE_TYPE, ContainerPipe.class);
+ for (Method method : PipeBuilder.class.getDeclaredMethods()){
+ PipeExecutor executor = method.getAnnotation(PipeExecutor.class);
+ if (executor != null){
+ registerPipe(executor.resourceType(), executor.pipeClass());
+ }
+ }
+ }
+
+ @Override
+ public Map getServiceUser() {
+ return serviceUser;
+ }
+
@Deactivate
public void deactivate(){
toggleJmxRegistration(null, PlumberMXBean.class.getName(), false);
@@ -441,7 +430,7 @@ public class PlumberImpl implements Plumber, JobConsumer, PlumberMXBean {
if (serviceUser != null) {
try (ResourceResolver resolver = factory.getServiceResourceResolver(serviceUser)) {
for (Iterator<Resource> resourceIterator = resolver.findResources(MONITORED_PIPES_QUERY, Query.XPATH); resourceIterator.hasNext(); ) {
- beans.add(new org.apache.sling.pipes.internal.PipeMonitor(this, getPipe(resourceIterator.next())));
+ beans.add(new PipeMonitor(this, getPipe(resourceIterator.next())));
}
} catch (LoginException e) {
log.error("unable to retrieve resolver for collecting exposed pipes", e);
diff --git a/src/main/java/org/apache/sling/pipes/internal/TraversePipe.java b/src/main/java/org/apache/sling/pipes/internal/TraversePipe.java
index 66f3bf9..42f19c0 100644
--- a/src/main/java/org/apache/sling/pipes/internal/TraversePipe.java
+++ b/src/main/java/org/apache/sling/pipes/internal/TraversePipe.java
@@ -45,6 +45,7 @@ public class TraversePipe extends BasePipe {
*
* @param plumber plumber
* @param resource configuration resource
+ * @param upperBindings super pipe's bindings
* @throws Exception in case configuration is not working
*/
public TraversePipe(Plumber plumber, Resource resource, PipeBindings upperBindings) throws Exception {
diff --git a/src/main/java/org/apache/sling/pipes/internal/WritePipe.java b/src/main/java/org/apache/sling/pipes/internal/WritePipe.java
index b2f1526..5e2293b 100644
--- a/src/main/java/org/apache/sling/pipes/internal/WritePipe.java
+++ b/src/main/java/org/apache/sling/pipes/internal/WritePipe.java
@@ -55,6 +55,7 @@ public class WritePipe extends BasePipe {
* public constructor
* @param plumber plumber instance
* @param resource configuration resource
+ * @param upperBindings super pipe's bindings
* @throws Exception bad configuration handling
*/
public WritePipe(Plumber plumber, Resource resource, PipeBindings upperBindings) throws Exception {
@@ -80,6 +81,7 @@ public class WritePipe extends BasePipe {
* @param key property to which value will be written
* @param expression configured value to write
* @return actual value to write to the resource
+ * @throws ScriptException in case value computation went wrong
*/
protected Object computeValue(Resource resource, String key, String expression) throws ScriptException {
Object value = bindings.instantiateObject((String) expression);
diff --git a/src/test/java/org/apache/sling/pipes/internal/GogoCommandsTest.java b/src/test/java/org/apache/sling/pipes/internal/GogoCommandsTest.java
new file mode 100644
index 0000000..ef838af
--- /dev/null
+++ b/src/test/java/org/apache/sling/pipes/internal/GogoCommandsTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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 org.apache.sling.api.resource.PersistenceException;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.pipes.AbstractPipeTest;
+import org.apache.sling.pipes.ExecutionResult;
+import org.apache.sling.pipes.PipeBuilder;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static org.junit.Assert.*;
+
+public class GogoCommandsTest extends AbstractPipeTest {
+
+ GogoCommands commands;
+
+ @Before
+ public void setup() throws PersistenceException {
+ super.setup();
+ commands = new GogoCommands();
+ commands.plumber = plumber;
+ }
+
+ @Test
+ public void testParseTokens(){
+ List<GogoCommands.Token> tokens = commands.parseTokens("some", "isolated", "items");
+ assertEquals("there should be 1 token", 1, tokens.size());
+ GogoCommands.Token token = tokens.get(0);
+ assertEquals("pipe key should be 'some'","some", token.pipeKey);
+ assertEquals("pipe args should be isolated, items", Arrays.asList("isolated","items"), token.args);
+ String tokenString = "first arg / second firstarg secondarg @ name second / third blah";
+ tokens = commands.parseTokens(tokenString.split("\\s"));
+ assertEquals("there should be 3 tokens", 3, tokens.size());
+ assertEquals("keys check", Arrays.asList("first","second", "third"), tokens.stream().map(t -> t.pipeKey).collect(Collectors.toList()));
+ assertEquals("params check", "second", tokens.get(1).options.name);
+ }
+
+ @Test
+ public void testSimpleExpression() throws Exception {
+ PipeBuilder builder = commands.parse(context.resourceResolver(),"echo","/content/fruits");
+ assertTrue("there should be a resource", builder.build().getOutput().hasNext());
+ }
+
+ @Test
+ public void testSimpleChainedConf() throws Exception {
+ PipeBuilder builder = commands.parse(context.resourceResolver(),"echo /content/fruits / write some=test key=value".split("\\s"));
+ assertNotNull("there should be a resource", builder.run());
+ ValueMap props = context.currentResource(PATH_FRUITS).getValueMap();
+ assertEquals("there should some=test", "test", props.get("some"));
+ assertEquals("there should key=value", "value", props.get("key"));
+ }
+
+ @Test
+ public void testOptions() {
+ String expected = "works";
+ String optionString = "@ name works @ path works @ expr works @ with one=works two=works @ outputs one=works two=works";
+ GogoCommands.Options options = commands.getOptions(optionString.split("\\s"));
+ assertEquals("check name", expected, options.name);
+ assertEquals("check expr", expected, options.expr);
+ assertEquals("check path", expected, options.path);
+ Map bindings = new HashMap();
+ CommandUtil.writeToMap(bindings, options.bindings);
+ assertEquals("check bindings first", expected, bindings.get("one"));
+ assertEquals("check bindings second", expected, bindings.get("two"));
+ assertNotNull("a writer should have been created", options.writer);
+ Map outputs = options.writer.getCustomOutputs();
+ assertEquals("check writer first", expected, outputs.get("one"));
+ assertEquals("check writer second", expected, outputs.get("two"));
+ }
+
+ @Test
+ public void testOptionsListsWithOneItem() {
+ String expected = "works";
+ String optionString = "@ with one=works @ outputs one=works";
+ GogoCommands.Options options = commands.getOptions(optionString.split("\\s"));
+ Map bindings = new HashMap();
+ CommandUtil.writeToMap(bindings, options.bindings);
+ assertEquals("check bindings first", expected, bindings.get("one"));
+ assertNotNull("a writer should have been created", options.writer);
+ Map outputs = options.writer.getCustomOutputs();
+ assertEquals("check writer first", expected, outputs.get("one"));
+ }
+
+ @Test
+ public void testChainedConfWithInternalOptions() throws Exception {
+ PipeBuilder builder = commands.parse(context.resourceResolver(),
+ "echo /content/fruits @ name fruits / write some=${path.fruits} key=value".split("\\s"));
+ assertNotNull("there should be a resource", builder.run());
+ ValueMap props = context.currentResource(PATH_FRUITS).getValueMap();
+ assertEquals("there should some=/content/fruits", PATH_FRUITS, props.get("some"));
+ assertEquals("there should key=value", "value", props.get("key"));
+ }
+
+ @Test
+ public void testExecuteWithWriter() throws Exception {
+ PipeBuilder builder = plumber.newPipe(context.resourceResolver()).echo("/content/${node}").$("nt:base");
+ String path = builder.build().getResource().getPath();
+ ExecutionResult result = commands.executeInternal(context.resourceResolver(), path, "@ outputs title=jcr:title desc=jcr:description @ with node=fruits");
+ }
+}
\ No newline at end of file