You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2015/12/07 15:19:52 UTC
svn commit: r1718377 [1/3] - in /sling/trunk/contrib/extensions/sling-pipes:
./ src/ src/main/ src/main/java/ src/main/java/org/
src/main/java/org/apache/ src/main/java/org/apache/sling/
src/main/java/org/apache/sling/pipes/ src/main/java/org/apache/sl...
Author: bdelacretaz
Date: Mon Dec 7 14:19:52 2015
New Revision: 1718377
URL: http://svn.apache.org/viewvc?rev=1718377&view=rev
Log:
SLING-5134 - new Sling Pipes module, donated by Nicolas Peltier, thanks!
Added:
sling/trunk/contrib/extensions/sling-pipes/ (with props)
sling/trunk/contrib/extensions/sling-pipes/.gitignore
sling/trunk/contrib/extensions/sling-pipes/README.md
sling/trunk/contrib/extensions/sling-pipes/pom.xml
sling/trunk/contrib/extensions/sling-pipes/src/
sling/trunk/contrib/extensions/sling-pipes/src/main/
sling/trunk/contrib/extensions/sling-pipes/src/main/java/
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/AuthorizablePipe.java
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/BasePipe.java
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/ContainerPipe.java
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/FilterPipe.java
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/JsonPipe.java
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/MovePipe.java
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/MultiPropertyPipe.java
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/ParentPipe.java
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/PathPipe.java
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/Pipe.java
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/PipeBindings.java
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/Plumber.java
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/PlumberServlet.java
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/ReferencePipe.java
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/RemovePipe.java
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/SlingQueryPipe.java
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/WritePipe.java
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/XPathPipe.java
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/impl/
sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/impl/PlumberImpl.java
sling/trunk/contrib/extensions/sling-pipes/src/test/
sling/trunk/contrib/extensions/sling-pipes/src/test/java/
sling/trunk/contrib/extensions/sling-pipes/src/test/java/org/
sling/trunk/contrib/extensions/sling-pipes/src/test/java/org/apache/
sling/trunk/contrib/extensions/sling-pipes/src/test/java/org/apache/sling/
sling/trunk/contrib/extensions/sling-pipes/src/test/java/org/apache/sling/pipes/
sling/trunk/contrib/extensions/sling-pipes/src/test/java/org/apache/sling/pipes/AbstractPipeTest.java
sling/trunk/contrib/extensions/sling-pipes/src/test/java/org/apache/sling/pipes/ContainerPipeTest.java
sling/trunk/contrib/extensions/sling-pipes/src/test/java/org/apache/sling/pipes/FilterPipeTest.java
sling/trunk/contrib/extensions/sling-pipes/src/test/java/org/apache/sling/pipes/JsonPipeTest.java
sling/trunk/contrib/extensions/sling-pipes/src/test/java/org/apache/sling/pipes/MovePipeTest.java
sling/trunk/contrib/extensions/sling-pipes/src/test/java/org/apache/sling/pipes/MultiPropertyPipeTest.java
sling/trunk/contrib/extensions/sling-pipes/src/test/java/org/apache/sling/pipes/PipeBindingsTest.java
sling/trunk/contrib/extensions/sling-pipes/src/test/java/org/apache/sling/pipes/PlumberServletTest.java
sling/trunk/contrib/extensions/sling-pipes/src/test/java/org/apache/sling/pipes/RemovePipeTest.java
sling/trunk/contrib/extensions/sling-pipes/src/test/java/org/apache/sling/pipes/SlingQueryPipeTest.java
sling/trunk/contrib/extensions/sling-pipes/src/test/java/org/apache/sling/pipes/WritePipeTest.java
sling/trunk/contrib/extensions/sling-pipes/src/test/java/org/apache/sling/pipes/dummies/
sling/trunk/contrib/extensions/sling-pipes/src/test/java/org/apache/sling/pipes/dummies/DummyNull.java
sling/trunk/contrib/extensions/sling-pipes/src/test/java/org/apache/sling/pipes/dummies/DummySearch.java
sling/trunk/contrib/extensions/sling-pipes/src/test/resources/
sling/trunk/contrib/extensions/sling-pipes/src/test/resources/container.json
sling/trunk/contrib/extensions/sling-pipes/src/test/resources/filter.json
sling/trunk/contrib/extensions/sling-pipes/src/test/resources/fruits.json
sling/trunk/contrib/extensions/sling-pipes/src/test/resources/json.json
sling/trunk/contrib/extensions/sling-pipes/src/test/resources/move.json
sling/trunk/contrib/extensions/sling-pipes/src/test/resources/multiProperty.json
sling/trunk/contrib/extensions/sling-pipes/src/test/resources/plumber.json
sling/trunk/contrib/extensions/sling-pipes/src/test/resources/remove.json
sling/trunk/contrib/extensions/sling-pipes/src/test/resources/slingQuery.json
sling/trunk/contrib/extensions/sling-pipes/src/test/resources/testSum.js
sling/trunk/contrib/extensions/sling-pipes/src/test/resources/users.json
sling/trunk/contrib/extensions/sling-pipes/src/test/resources/write.json
Propchange: sling/trunk/contrib/extensions/sling-pipes/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Mon Dec 7 14:19:52 2015
@@ -0,0 +1,14 @@
+target
+bin
+derby.log
+*.iml
+*.ipr
+*.iws
+.settings
+.project
+.classpath
+.externalToolBuilders
+maven-eclipse.xml
+
+
+
Added: sling/trunk/contrib/extensions/sling-pipes/.gitignore
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/sling-pipes/.gitignore?rev=1718377&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/sling-pipes/.gitignore (added)
+++ sling/trunk/contrib/extensions/sling-pipes/.gitignore Mon Dec 7 14:19:52 2015
@@ -0,0 +1,8 @@
+target
+.idea
+.classpath
+.project
+.settings
+*.iml
+bin
+.DS_Store
\ No newline at end of file
Added: sling/trunk/contrib/extensions/sling-pipes/README.md
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/sling-pipes/README.md?rev=1718377&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/sling-pipes/README.md (added)
+++ sling/trunk/contrib/extensions/sling-pipes/README.md Mon Dec 7 14:19:52 2015
@@ -0,0 +1,344 @@
+# sling-pipes
+tool for doing extract - transform - load operations through a resource tree configuration
+
+often one-shot data transformations need sample code to be written & executed. This tiny tool set intends to provide ability to do such transformations with proven & reusable blocks called pipes, streaming resources from one to the other.
+
+## What is a pipe
+
+```
+ getOutputBinding
+ ^
+ |
+getInput +---+---+ getOutput
+ | |
+ +----> Pipe +---->
+ | |
+ +-------+
+```
+A sling pipe is essentially a sling resource stream:
+* it provides an output as a sling resource iterator
+* it gets its input either from a configured path, either, if its chained (see container pipes below), from another pipe's output
+* each pipe can have additional dynamic inputs using other's bindings, and outputing its own bindings
+
+At the moment, there are 3 types of pipes to consider:
+* "reader" pipes, that will just output a set of resource depending on the input
+* "writer" pipes, that write to the repository, depending on configuration and output
+* "container" pipes, that contains pipes, and whose job is to chain their execution : input is the input of their first pipe,
+ output is the output of the last pipe it contains.
+
+A `Plumber` osgi service is provided to help getting & executing pipes.
+
+## Registered Pipes
+a pipe configuration is a jcr node, with:
+* `sling:resourceType` property, which must be a pipe type registered by the plumber
+* `name` property, that will be used in bindings as an id, and will be the key for the output bindings (default value being a value map of the current output resource). Note that the node name will be used in case no name is provided.
+* `path` property, if configured, will override upstream's pipe output as an input.
+* `expr` property, expression through which the pipe will execute (depending on the type)
+* `additionalBinding` is a node you can add to set "global" bindings (property=value) in pipe execution
+* `additionalScripts` is a multi value property to declare scripts that can be reused in expressions
+* `conf` optional child node that contains addition configuration of the pipe (depending on the type)
+
+### readers
+
+#### Base pipe
+rather dummy pipe, outputs what is in input (so what is configured in path). Handy for doing some test mostly, and giving basic functionalities to others that inherit from it
+* `sling:resourceType` is `slingPipes/base`
+
+#### SlingQuery Pipe
+executes $(getInput()).children(expression)
+* `sling:resourceType` is `slingPipes/slingQuery`
+* `expr` mandatory property, contains slingQuery expression through which getInput()'s children will be computed to getOutput()
+
+#### JsonPipe
+feeds bindings with remote json
+* `sling:resourceType` is `slingPipes/json`
+* `expr` mandatory property contains url that will be called, the json be sent to the output bindings, getOutput = getInput.
+An empty url or a failing url will block the pipe at that given place.
+
+#### MultiPropertyPipe
+iterates through values of input multi value property and write them to bindings
+* `sling:resourceType` is `slingPipes/multiProperty`
+* `path` should be the path of a mv property
+
+#### XPathPipe
+retrieve resources resulting of an xpath query
+* `sling:resourceType` is `slingPipes/xpath`
+* `expr` should be a valid xpath query
+
+### JsonPipe
+feeds bindings with remote json
+* `sling:resourceType` is `slingPipes/json`
+* `expr` mandatory property contains url that will be called, the json be sent to the output bindings, getOutput = getInput.
+An empty url or a failing url will block the pipe at that given place.
+
+#### AuthorizablePipe
+retrieve authorizable resource corresponding to the id passed in expression, or if not found (or void expression),
+from the input path, output the found authorizable's resource
+* `sling:resourceType` is `slingPipes/authorizable`
+* `expr` should be an authorizable id, or void (but then input should be an authorizable)
+* `autoCreateGroup` (boolean) if autorizable id is here, but the authorizable not present, then create group with given id (in that case, considered as a write pipe)
+* `addMembers` (stringified json array) if authorizable is a group, add instanciated members to it (in that case, considered as a write pipe)
+* `addToGroup` (expression) add found authorizable to instanciated group (in that case, considered as a write pipe)
+* `bindMembers` (boolean) if found authorizable is a group, bind the members (in that case, considered as a write pipe)
+
+#### ParentPipe
+outputs the parent resource of input resource
+* `sling:resourceType` is `slingPipes/parent`
+
+#### FilterPipe
+outputs the input resource if its matches its configuration
+* `sling:resourceType` is `slingPipes/filter`
+* `conf` node tree that will be tested against the current input of the pipe, each `/conf/sub@prop=value` will triggers a test
+on `./sub@prop` property of the current input, testing if its value matches `value` regex. If the special `slingPipesFilter_noChildren=${true}`
+property is there with the value instantiated as a true boolean, then filter will pass if corresponding node has no children.
+
+### containers
+#### Container Pipe
+assemble a sequence of pipes
+* `sling:resourceType` is `slingPipes/container`
+* `conf` node contains child pipes' configurations, that will be configured in the order they are found (note you should use sling:OrderedFolder)
+
+#### ReferencePipe
+execute the pipe referenced in path property
+* `sling:resourceType` is `slingPipes/reference`
+* `path` path of the referenced pipe
+
+### writers
+
+#### Write Pipe
+writes given properties to current input
+* `sling:resourceType` is `slingPipes/slingQuery`
+* `conf` node tree that will be copied to the current input of the pipe, each node's properties
+names and value will be written to the input resource. Input resource will be outputed.
+
+### MovePipe
+JCR move of current input to target path (can be a node or a property)
+* `sling:resourceType` is `slingPipes/mv`
+* `expr` target path, note that parent path must exists
+
+#### RemovePipe
+removes the input resource, returns the parent, regardless of the resource being a node, or
+a property
+* `sling:resourceType` is `slingPipes/rm`
+* `conf` node tree that will be used to filter relative properties & subtrees to the current resource to remove.
+A subnode is considered to be removed if it has no property configured, nore any child.
+
+#### PathPipe
+get or create path given in expression
+* `sling:resourceType` is `slingPipes/path`
+* `nodeType` node type of the intermediate nodes to create
+* `autosave` should save at each creation (will make things slow, but sometimes you don't have choice)
+
+## Making configuration dynamic with pipe bindings
+in order to make things interesting, most of the configurations are javascript template strings, hence valid js expressions reusing bindings (from configuration, or other pipes).
+
+Following configurations are evaluated:
+* `path`
+* `expr`
+* name/value of each property of some pipes (write, remove)
+
+you can use name of previous pipes in the pipe container, or the special binding `path`, where `path.previousPipe`
+is the path of the current resource of previous pipe named `previousPipe`
+
+global bindings can be set at pipe execution, external scripts can be added to the execution as well (see pipe
+ configurations)
+
+## How to execute a pipe
+for now it's possible to execute Pipes through GET (read) or POST (read/write) commands:
+
+### Request Path
+- either you'll need to create a slingPipes/plumber resource, say `etc/pipes` and then to execute
+```
+curl -u admin:admin -F "path=/etc/pipes/mySamplePipe" http://localhost:8080/etc/pipes.json
+```
+- either you execute the request directly on the pipe Path, e.g.
+```
+curl -u admin:admin http://localhost:8080/etc/pipes/mySamplePipe.json
+```
+which will return you the path of the pipes that have been through the output of the configured pipe.
+
+### Request Parameter `binding`
+
+you can add as `bindings` parameter a json object of global bindings you want to add for the execution of the pipe
+
+e.g.
+
+```
+ curl -u admin:admin -F "path=/etc/pipes/test" -F "bindings={testBinding:'foo'}" http://localhost:4502/etc/pipes.json
+```
+
+will returns something like
+
+```
+["/one/output/resource", "another/one"]
+```
+
+### Request Parameter `writer`
+
+you can add as `writer` parameter a json object as a pattern to the result you want to have. The values of the json
+object are expressions and can reuse each pipe's subpipe binding.
+Note
+this works only if the pipe called is a container
+pipe.
+
+e.g.
+
+```
+curl -u admin:admin http://localhost:4502/etc/pipes/users.json?writer={"user":"${user.fullName}"}
+```
+
+will returns something similar to
+
+```
+[{'user':'John Smith','path':'/home/users/q/q123jk1UAZS'},{'user':'John Doe','path':'/home/users/q/q153jk1UAZS'}]
+```
+
+### Request Parameter `dryRun`
+if parameter dryRun is set to true, and the executed pipe is supposed to modify content, it will log (at best it can) the change it *would* have done, without doing anything
+
+## sample configurations
+
+### slingQuery | write
+this pipe parse all profile nodes, and
+```
+{
+ "sling:resourceType":"slingPipes/container",
+ "name":"Dummy User prefix Sample",
+ "jcr:description":"prefix all full names of profile with "Mr" or "Ms" depending on gender",
+ "conf":{
+ "profile": {
+ "sling:resourceType":"slingPipes/slingQuery",
+ "expr":"nt:unstructured#profile",
+ "path":"/home/users"
+ },
+ "writeFullName": {
+ "sling:resourceType":"slingPipes/write",
+ "conf": {
+ "fullName":"${(profile.gender === 'female' ? 'Ms ' + profile.fullName : 'Mr ' + profile.fullName)}",
+ "generatedBy":"slingPipes"
+ }
+ }
+ }
+}
+```
+
+### slingQuery | multiProperty | authorizable | write
+```
+{
+ "jcr:primaryType": "sling:Folder",
+ "jcr:description": "move badge<->user relation ship from badge MV property to a user MV property"
+ "name": "badges",
+ "sling:resourceType": "slingPipes/container",
+ "conf": {
+ "jcr:primaryType": "sling:OrderedFolder",
+ "badge": {
+ "jcr:primaryType": "sling:Folder",
+ "jcr:description": "outputs all badge component resources",
+ "expr": "[sling:resourceType=myApp/components/badge]",
+ "path": "/etc/badges/badges-admin/jcr:content",
+ "sling:resourceType": "slingPipes/slingQuery"
+ },
+ "profile": {
+ "jcr:primaryType": "sling:Folder",
+ "jcr:description": "retrieve all user ids from a mv property",
+ "path": "${path.badge}/profiles",
+ "sling:resourceType": "slingPipes/multiProperty"
+ },
+ "user": {
+ "jcr:primaryType": "sling:OrderedFolder",
+ "jcr:description": "outputs user resource",
+ "expr": "profile",
+ "sling:resourceType": "slingPipes/authorizable"
+ },
+ "write": {
+ "jcr:primaryType": "sling:OrderedFolder",
+ "jcr:descritption": "patches the badge path to the badges property of the user profile"
+ "path": "${path.user}/profile",
+ "sling:resourceType": "slingPipes/write",
+ "conf": {
+ "jcr:primaryType": "nt:unstructured",
+ "badges": "+[${path.badge}]"
+ }
+ }
+ }
+ }
+```
+
+### xpath |Â json |Â write
+this use case is for completing repository profiles with external system's data (that has an json api)
+```
+{
+ "jcr:primaryType": "nt:unstructured",
+ "jcr:description": "this pipe retrieves json info from an external system and writes them to the user profile, uses moment.js, it
+ distributes modified resources using publish distribution agent",
+ "sling:resourceType": "slingPipes/container",
+ "distribution.agent": "publish",
+ "additionalScripts": "/etc/source/moment.js",
+ "conf": {
+ "jcr:primaryType": "sling:OrderedFolder",
+ "profile": {
+ "jcr:primaryType": "sling:OrderedFolder",
+ "expr": "/jcr:root/home/users//element(profile,nt:unstructured)[@uid]",
+ "jcr:description": "query all user profile nodes",
+ "sling:resourceType": "slingPipes/xpath"
+ },
+ "json": {
+ "jcr:primaryType": "sling:OrderedFolder",
+ "expr": "${(profile.uid ? 'https://my.external.system.corp.com/profiles/' + profile.uid.substr(0,2) + '/' + profile.uid + '.json' : '')",
+ "jcr:description": "retrieves json information relative to the given profile, if the uid is not found, expr is empty: the pipe will do nothing",
+ "sling:resourceType": "slingPipes/json"
+ },
+ "write": {
+ "jcr:primaryType": "sling:OrderedFolder",
+ "path": "path.profile",
+ "jcr:description": "write json information to the profile node",
+ "sling:resourceType": "slingPipes/write",
+ "conf": {
+ "jcr:primaryType": "sling:OrderedFolder",
+ "jcr:createdBy": "admin",
+ "background": "${json.opt('background')}",
+ "about": "${json.opt('about')}",
+ "jcr:created": "Fri Jul 03 2015 15:32:22 GMT+0200",
+ "birthday": "${(json.opt('birthday') ? moment(json.opt('birthday'), \"MMMM DD\").toDate() : '')}",
+ "mobile": "${json.opt('mobile')}"
+ }
+ }
+ }
+ }
+```
+
+### xpath |Â parent |Â rm
+```
+{
+ "jcr:primaryType": "nt:unstructured",
+ "jcr:description": "this pipe removes user with bad property in their profile",
+ "sling:resourceType": "slingPipes/container",
+ "conf": {
+ "jcr:primaryType": "sling:OrderedFolder",
+ "profile": {
+ "jcr:primaryType": "sling:OrderedFolder",
+ "expr": "/jcr:root/home/users//element(profile,nt:unstructured)[@bad]",
+ "jcr:description": "query all user profile nodes with bad properties",
+ "sling:resourceType": "slingPipes/xpath"
+ },
+ "parent": {
+ "jcr:primaryType": "sling:OrderedFolder",
+ "jcr:description": "get the parent node (user node)",
+ "sling:resourceType": "slingPipes/parent"
+ },
+ "rm": {
+ "jcr:primaryType": "sling:OrderedFolder",
+ "jcr:description": "remove it",
+ "sling:resourceType": "slingPipes/rm",
+ }
+ }
+}
+```
+
+some other samples are in https://github.com/npeltier/sling-pipes/tree/master/src/test/
+
+# Compatibility
+For running this tool on a sling instance you need:
+- java 8 (Nashorn is used for expression)
+- slingQuery (3.0.0) (used in SlingQueryPipe)
+- jackrabbit api (2.7.5+) (used in AuthorizablePipe)
Added: sling/trunk/contrib/extensions/sling-pipes/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/sling-pipes/pom.xml?rev=1718377&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/sling-pipes/pom.xml (added)
+++ sling/trunk/contrib/extensions/sling-pipes/pom.xml Mon Dec 7 14:19:52 2015
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>sling</artifactId>
+ <version>23</version>
+ <relativePath/>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>org.apache.sling.pipes</artifactId>
+ <packaging>bundle</packaging>
+ <version>0.0.8-SNAPSHOT</version>
+
+ <name>Apache Sling Pipes</name>
+ <description>bulk content changes tool</description>
+ <properties>
+ <sling.java.version>8</sling.java.version>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-scr-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ org.apache.sling.distribution;resolution:=optional,
+ org.apache.sling.query;version=3.0.0,
+ *
+ </Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <version>2.5</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <version>4.2.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>4.2.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.jcr</groupId>
+ <artifactId>jcr</artifactId>
+ <version>2.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.api</artifactId>
+ <version>2.9.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.6.4</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.scr.annotations</artifactId>
+ <version>1.9.8</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.query</artifactId>
+ <version>3.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.jackrabbit</groupId>
+ <artifactId>jackrabbit-api</artifactId>
+ <version>2.7.5</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.distribution.api</artifactId>
+ <version>0.1.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ <version>2.5</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-httpclient</groupId>
+ <artifactId>commons-httpclient</artifactId>
+ <version>3.1</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.4</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- testing dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.11</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.testing.sling-mock</artifactId>
+ <version>1.3.0</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.json</artifactId>
+ <version>2.0.6</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
Added: sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/AuthorizablePipe.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/AuthorizablePipe.java?rev=1718377&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/AuthorizablePipe.java (added)
+++ sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/AuthorizablePipe.java Mon Dec 7 14:19:52 2015
@@ -0,0 +1,204 @@
+/*
+ * 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.commons.lang.StringUtils;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.commons.json.JSONArray;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.RepositoryException;
+import java.util.Collections;
+import java.util.Iterator;
+
+/**
+ * pipe that outputs an authorizable resource based on the id set in expr
+ */
+public class AuthorizablePipe extends BasePipe {
+ private static Logger logger = LoggerFactory.getLogger(AuthorizablePipe.class);
+ public static final String RESOURCE_TYPE = "slingPipes/authorizable";
+ public static final String PN_AUTOCREATEGROUP = "createGroup";
+ public static final String PN_ADDTOGROUP = "addToGroup";
+ public static final String PN_ADDMEMBERS = "addMembers";
+ public static final String PN_BINDMEMBERS = "bindMembers";
+
+ UserManager userManager;
+ ResourceResolver resolver;
+ boolean autoCreateGroup;
+ boolean bindMembers;
+ String addToGroup;
+ String addMembers;
+ Object outputBinding;
+
+ @Override
+ public Object getOutputBinding() {
+ if (outputBinding != null) {
+ return outputBinding;
+ }
+ return super.getOutputBinding();
+ }
+
+ @Override
+ public boolean modifiesContent() {
+ return autoCreateGroup || StringUtils.isNotBlank(addToGroup) || StringUtils.isNotBlank(addMembers);
+ }
+
+ public AuthorizablePipe(Plumber plumber, Resource resource) throws Exception {
+ super(plumber, resource);
+ resolver = resource.getResourceResolver();
+ userManager = resolver.adaptTo(UserManager.class);
+ if (getConfiguration() != null) {
+ ValueMap properties = getConfiguration().adaptTo(ValueMap.class);
+ autoCreateGroup = properties.get(PN_AUTOCREATEGROUP, false);
+ bindMembers = properties.get(PN_BINDMEMBERS, false);
+ addToGroup = properties.get(PN_ADDTOGROUP, String.class);
+ addMembers = properties.get(PN_ADDMEMBERS, String.class);
+ }
+ }
+
+ @Override
+ public Iterator<Resource> getOutput() {
+ try {
+ Authorizable auth = getAuthorizable();
+ if (auth != null) {
+ logger.debug("Retrieved authorizable {}", auth.getID());
+ if (StringUtils.isNotBlank(addToGroup)){
+ addToGroup(auth);
+ }
+ if (StringUtils.isNotBlank(addMembers)){
+ addMembers(auth);
+ }
+ if (bindMembers){
+ bindMembers(auth);
+ }
+ Resource resource = resolver.getResource(auth.getPath());
+ return Collections.singleton(resource).iterator();
+ }
+ } catch (Exception e){
+ }
+ return EMPTY_ITERATOR;
+ }
+
+ /**
+ * Returns the authorizable configured by its expression, creating it if
+ * not present and if <code>autoCreateGroup</code> is set to true, or, if
+ * no expression, tries to resolve getInput() as an authorizable
+ * @return
+ * @throws RepositoryException
+ */
+ protected Authorizable getAuthorizable() {
+ Authorizable auth = null;
+ try {
+ String authId = getExpr();
+ if (StringUtils.isNotBlank(authId)) {
+ logger.debug("try to find authorizable {}", authId);
+ auth = userManager.getAuthorizable(authId);
+ if (auth == null && autoCreateGroup) {
+ logger.info("authorizable {}Â does not exist, creating", authId);
+ auth = userManager.createGroup(authId);
+ }
+ } else {
+ Resource resource = getInput();
+ if (resource != null) {
+ auth = userManager.getAuthorizableByPath(resource.getPath());
+ }
+ }
+ } catch (Exception e){
+ logger.error("unable to output authorizable based on configuration", e);
+ }
+ return auth;
+ }
+
+ /**
+ * Add current authorizable to configured addToGroup expression (should resolve as a group id)
+ * @param auth
+ */
+ protected void addToGroup(Authorizable auth){
+ try {
+ //if addToGroup is set to true, we try to find the corresponding
+ //group and to add current auth to it as a member
+ String groupId = bindings.instantiateExpression(addToGroup);
+ Authorizable groupAuth = (Group) userManager.getAuthorizable(groupId);
+ if (groupAuth != null && groupAuth.isGroup()) {
+ logger.info("adding {}Â to {}", auth.getID(), groupId);
+ if (! isDryRun()) {
+ ((Group) groupAuth).addMember(auth);
+ }
+ }
+ } catch (Exception e){
+ logger.error("Unable to add current authorizable to group {}", addToGroup, e);
+ }
+ }
+
+ /**
+ * Add to current authorizable (that should be a group) the configured members in addMembers expression
+ * @param auth
+ */
+ protected void addMembers(Authorizable auth) {
+ try {
+ if (auth.isGroup()) {
+ Group group = (Group)auth;
+ String uids = bindings.instantiateExpression(addMembers);
+ JSONArray array = new JSONArray(uids);
+ for (int index = 0; index < array.length(); index ++){
+ String uid = array.getString(index);
+ Authorizable member = userManager.getAuthorizable(uid);
+ if (member != null) {
+ logger.info("adding {} to group {}", member.getID(), group.getID());
+ if (!isDryRun()) {
+ group.addMember(member);
+ }
+ } else {
+ logger.error("computed uid {} doesn't exist, doing nothing", uid);
+ }
+ }
+ } else {
+ logger.error("{}Â is not a group, can't add members", auth.getID());
+ }
+ } catch (Exception e){
+ logger.error("unable to add members {}", addMembers, e);
+ }
+ }
+
+ /**
+ * add current group's members to the bindings
+ * @param auth
+ */
+ protected void bindMembers(Authorizable auth){
+ try {
+ if (auth.isGroup()){
+ Group group = (Group)auth;
+ Iterator<Authorizable> memberIterator = group.getMembers();
+ JSONArray array = new JSONArray();
+ while (memberIterator.hasNext()){
+ array.put(memberIterator.next().getID());
+ }
+ outputBinding = array.toString();
+ } else {
+ logger.error("{}Â is not a group, unable to bind members", auth.getID());
+ }
+ } catch (Exception e){
+ logger.error("unable to bind members");
+ }
+ }
+}
Added: sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/BasePipe.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/BasePipe.java?rev=1718377&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/BasePipe.java (added)
+++ sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/BasePipe.java Mon Dec 7 14:19:52 2015
@@ -0,0 +1,186 @@
+/*
+ * 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.commons.lang.StringUtils;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ValueMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * provides generic utilities for a pipe
+ */
+public class BasePipe implements Pipe {
+ Logger logger = LoggerFactory.getLogger(BasePipe.class);
+ public static final String RESOURCE_TYPE = "slingPipes/base";
+ protected static final String DRYRUN_KEY = "dryRun";
+ protected static final String DRYRUN_EXPR = "${" + DRYRUN_KEY + "}";
+
+ protected ResourceResolver resolver;
+ protected ValueMap properties;
+ protected Resource resource;
+ protected ContainerPipe parent;
+ protected String distributionAgent;
+ protected PipeBindings bindings;
+
+ // used by pipes using complex JCR configurations
+ public static final List<String> IGNORED_PROPERTIES = Arrays.asList(new String[]{"jcr:lastModified", "jcr:primaryType", "jcr:created", "jcr:createdBy"});
+
+
+ protected Boolean dryRunObject;
+
+ @Override
+ public ContainerPipe getParent() {
+ return parent;
+ }
+
+ @Override
+ public void setParent(ContainerPipe parent) {
+ this.parent = parent;
+ }
+
+ protected Plumber plumber;
+
+ private String name;
+
+ public BasePipe(Plumber plumber, Resource resource) throws Exception {
+ this.resource = resource;
+ properties = resource.adaptTo(ValueMap.class);
+ resolver = resource.getResourceResolver();
+ this.plumber = plumber;
+ name = properties.get(PN_NAME, resource.getName());
+ distributionAgent = properties.get(PN_DISTRIBUTION_AGENT, String.class);
+ bindings = new PipeBindings(resource);
+ }
+
+ public boolean isDryRun() {
+ if (dryRunObject == null) {
+ Object run = bindings.isBindingDefined(DRYRUN_KEY) ? bindings.instantiateObject(DRYRUN_EXPR) : false;
+ dryRunObject = run != null && run instanceof Boolean ? (Boolean)run : false;
+ }
+ boolean dryRun = dryRunObject != null ? dryRunObject : false;
+ return dryRun;
+ }
+
+ public String toString() {
+ return name + " " + "(path: " + resource.getPath() + ", dryRun: " + isDryRun() + ", modifiesContent: " + modifiesContent() + ")";
+ }
+
+ @Override
+ public boolean modifiesContent() {
+ return false;
+ }
+
+ public String getName(){
+ return name;
+ }
+
+ /**
+ * Get pipe's expression, instanciated or not
+ * @return
+ */
+ public String getExpr(){
+ String rawExpression = properties.get(PN_EXPR, "");
+ return bindings.instantiateExpression(rawExpression);
+ }
+
+ /**
+ * Get pipe's path, instanciated or not
+ * @return
+ */
+ public String getPath() {
+ String rawPath = properties.get(PN_PATH, "");
+ return bindings.instantiateExpression(rawPath);
+ }
+
+ @Override
+ public Resource getConfiguredInput() {
+ Resource configuredInput = null;
+ String path = getPath();
+ if (StringUtils.isNotBlank(path)){
+ configuredInput = resolver.getResource(path);
+ if (configuredInput == null) {
+ logger.warn("configured path {} is not found, expect some troubles...", path);
+ }
+ }
+ return configuredInput;
+ }
+
+ @Override
+ public Resource getInput() {
+ Resource resource = getConfiguredInput();
+ if (resource == null && parent != null){
+ Pipe previousPipe = parent.getPreviousPipe(this);
+ if (previousPipe != null) {
+ return bindings.getExecutedResource(previousPipe.getName());
+ }
+ }
+ return resource;
+ }
+
+
+ @Override
+ public Object getOutputBinding() {
+ if (parent != null){
+ Resource resource = bindings.getExecutedResource(getName());
+ if (resource != null) {
+ return resource.adaptTo(ValueMap.class);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public PipeBindings getBindings() {
+ return bindings;
+ }
+
+ @Override
+ public void setBindings(PipeBindings bindings) {
+ this.bindings = bindings;
+ }
+
+ /**
+ * default execution, just returns current resource
+ * @return
+ */
+ public Iterator<Resource> getOutput(){
+ return Collections.singleton(getInput()).iterator();
+ }
+
+ /**
+ * Get configuration node
+ * @return
+ */
+ public Resource getConfiguration() {
+ return resource.getChild(NN_CONF);
+ }
+
+ @Override
+ public String getDistributionAgent() {
+ return distributionAgent;
+ }
+
+ public static final Iterator<Resource> EMPTY_ITERATOR = Collections.emptyIterator();
+}
Added: sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/ContainerPipe.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/ContainerPipe.java?rev=1718377&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/ContainerPipe.java (added)
+++ sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/ContainerPipe.java Mon Dec 7 14:19:52 2015
@@ -0,0 +1,227 @@
+/*
+ * 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 org.apache.sling.api.resource.ValueMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.script.Bindings;
+import javax.script.Invocable;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import javax.script.SimpleScriptContext;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This pipe executes the pipes it has in its configuration, chaining their result, and
+ * modifying each contained pipe's expression with its context
+ */
+public class ContainerPipe extends BasePipe {
+ private static final Logger log = LoggerFactory.getLogger(ContainerPipe.class);
+
+ public static final String RESOURCE_TYPE = "slingPipes/container";
+
+ Map<String, Pipe> pipes = new HashMap<>();
+
+ List<Pipe> pipeList = new ArrayList<>();
+
+ List<Pipe> reversePipeList = new ArrayList<>();
+
+ public ContainerPipe(Plumber plumber, Resource resource) throws Exception{
+ super(plumber, resource);
+ for (Iterator<Resource> childPipeResources = getConfiguration().listChildren(); childPipeResources.hasNext();){
+ Resource pipeResource = childPipeResources.next();
+ Pipe pipe = plumber.getPipe(pipeResource);
+ if (pipe == null) {
+ log.error("configured pipe {} is either not registered, or not computable by the plumber", pipeResource.getPath());
+ } else {
+ pipe.setParent(this);
+ pipe.setBindings(bindings);
+ pipes.put(pipe.getName(), pipe);
+ pipeList.add(pipe);
+ reversePipeList.add(pipe);
+ }
+ }
+ Collections.reverse(reversePipeList);
+ }
+
+ @Override
+ public boolean modifiesContent() {
+ for (Pipe pipe : pipes.values()){
+ if (pipe.modifiesContent()){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Iterator<Resource> getOutput() {
+ if (pipeList.size() == 1) {
+ //corner case with only one element: no need to have a container resource iterator
+ return pipeList.iterator().next().getOutput();
+ }
+ return new ContainerResourceIterator(this);
+ }
+
+ /**
+ * Returns the pipe immediately before the given pipe, null if it's the first
+ * @param pipe
+ * @return
+ */
+ public Pipe getPreviousPipe(Pipe pipe){
+ Pipe previousPipe = null;
+ for (Pipe candidate : pipeList){
+ if (candidate.equals(pipe)){
+ return previousPipe;
+ }
+ previousPipe = candidate;
+ }
+ return null;
+ }
+
+ /**
+ * Return the first pipe in the container
+ * @return
+ */
+ public Pipe getFirstPipe() {
+ return pipeList.iterator().next();
+ }
+
+ /**
+ * Return the last pipe in the container
+ * @return
+ */
+ public Pipe getLastPipe() {
+ return reversePipeList.iterator().next();
+ }
+
+ public Resource getOuputResource() {
+ return bindings.getExecutedResource(getLastPipe().getName());
+ }
+
+ /**
+ * Container Iterator, that iterates through the whole chain
+ * of pipes, returning the result resources of the end of the chain
+ */
+ static class ContainerResourceIterator implements Iterator<Resource> {
+ /**
+ * map name -> iterator
+ */
+ Map<Pipe, Iterator<Resource>> iterators;
+
+ /**
+ * container pipe
+ */
+ ContainerPipe container;
+
+ PipeBindings bindings;
+
+ boolean computedCursor = false;
+ boolean hasNext = false;
+ int cursor = 0;
+
+ ContainerResourceIterator(ContainerPipe containerPipe) {
+ container = containerPipe;
+ bindings = container.bindings;
+ iterators = new HashMap<>();
+ Pipe firstPipe = container.getFirstPipe();
+ //we initialize the first iterator the only one not to be updated
+ iterators.put(firstPipe, firstPipe.getOutput());
+ }
+
+ /**
+ * go up and down the container iterators until cursor is at 0 (first pipe) with no
+ * more resources, or at length - 1 (last pipe) with a next one
+ * @return
+ */
+ private boolean updateCursor(){
+ Pipe currentPipe = container.pipeList.get(cursor);
+ Iterator<Resource> it = iterators.get(currentPipe);
+ do {
+ // go up to at best reach the last pipe, updating iterators & bindings of the
+ // all intermediates, if an intermediate pipe is not outputing anything
+ // anymore, stop.
+ while (it.hasNext() && cursor < container.pipeList.size() - 1) {
+ Resource resource = it.next();
+ bindings.updateBindings(currentPipe, resource);
+ //now we update the following pipe output with that new context
+ Pipe nextPipe = container.pipeList.get(++cursor);
+ iterators.put(nextPipe, nextPipe.getOutput());
+ currentPipe = nextPipe;
+ it = iterators.get(currentPipe);
+ }
+ //go down (or stay) to the first pipe having a next item
+ while (!it.hasNext() && cursor > 0) {
+ currentPipe = container.pipeList.get(--cursor);
+ it = iterators.get(currentPipe);
+ }
+ } while (it.hasNext() && cursor < container.pipeList.size() - 1);
+ //2 choices here:
+ // either cursor is at 0 with no resource left: end,
+ // either cursor is on last pipe with a resource left: hasNext
+ return cursor > 0;
+ }
+
+ /**
+ * we need to find the first "path" from first pipe to the last
+ * where each pipe returns something, if no "path", this pipe is
+ * done, other wise we must have updated iterators (next is allowed
+ * up to the pipe before the last), and return true
+ * @return
+ */
+ @Override
+ public boolean hasNext() {
+ if (! computedCursor) {
+ hasNext = updateCursor();
+ }
+ return hasNext;
+ }
+
+ @Override
+ public Resource next() {
+ hasNext = computedCursor && hasNext || hasNext();
+ if (hasNext) {
+ computedCursor = false;
+ hasNext = false;
+ Resource resource = iterators.get(container.getLastPipe()).next();
+ bindings.updateBindings(container.getLastPipe(), resource);
+ return resource;
+ }
+ return null;
+ }
+
+ @Override
+ public void remove() {
+
+ }
+ }
+
+}
Added: sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/FilterPipe.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/FilterPipe.java?rev=1718377&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/FilterPipe.java (added)
+++ sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/FilterPipe.java Mon Dec 7 14:19:52 2015
@@ -0,0 +1,103 @@
+/*
+ * 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 org.apache.sling.api.resource.ValueMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.regex.Pattern;
+
+/**
+ * intends to output the input only if configured conditions are fulfilled
+ */
+public class FilterPipe extends BasePipe {
+ private static Logger logger = LoggerFactory.getLogger(FilterPipe.class);
+ public static final String RESOURCE_TYPE = "slingPipes/filter";
+ public static final String PREFIX_FILTER = "slingPipesFilter_";
+ public static final String PN_NOCHILDREN = PREFIX_FILTER + "noChildren";
+ public static final String PN_TEST = PREFIX_FILTER + "test";
+
+ public FilterPipe(Plumber plumber, Resource resource) throws Exception {
+ super(plumber, resource);
+ }
+
+ boolean propertiesPass(ValueMap current, ValueMap filter){
+ if (filter.containsKey(PN_TEST)){
+ if (!(Boolean) bindings.instantiateObject(filter.get(PN_TEST, "${false}"))){
+ return false;
+ }
+ }
+ for (String key : filter.keySet()){
+ if (! IGNORED_PROPERTIES.contains(key) && !key.startsWith(PREFIX_FILTER)){
+ Pattern pattern = Pattern.compile(filter.get(key, String.class));
+ if (!pattern.matcher(current.get(key, String.class)).matches()){
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ boolean filterPasses(Resource currentResource, Resource filterResource){
+ try {
+ ValueMap current = currentResource.adaptTo(ValueMap.class);
+ ValueMap filter = filterResource.adaptTo(ValueMap.class);
+ if (propertiesPass(current, filter)) {
+ Node currentNode = currentResource.adaptTo(Node.class);
+ boolean noChildren = (Boolean) bindings.instantiateObject(filter.get(PN_NOCHILDREN, "${false}"));
+ if (noChildren) {
+ return !currentNode.hasNodes();
+ } else {
+ Node filterNode = filterResource.adaptTo(Node.class);
+ boolean returnValue = true;
+ for (NodeIterator children = filterNode.getNodes(); returnValue && children.hasNext();){
+ String childName = children.nextNode().getName();
+ if (!currentNode.hasNode(childName)){
+ return false;
+ } else {
+ returnValue &= filterPasses(currentResource.getChild(childName), filterResource.getChild(childName));
+ }
+ }
+ return returnValue;
+ }
+ }
+ } catch (Exception e){
+ logger.error("error when executing filter", e);
+ }
+ return false;
+ }
+
+ @Override
+ public Iterator<Resource> getOutput() {
+ Resource resource = getInput();
+ if (resource != null){
+ if (filterPasses(resource, getConfiguration())){
+ logger.debug("filter passes for {}", resource.getPath());
+ return super.getOutput();
+ } else {
+ logger.info("{} got filtered out", resource.getPath());
+ }
+ }
+ return Collections.emptyIterator();
+ }
+}
Added: sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/JsonPipe.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/JsonPipe.java?rev=1718377&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/JsonPipe.java (added)
+++ sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/JsonPipe.java Mon Dec 7 14:19:52 2015
@@ -0,0 +1,164 @@
+/*
+ * 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.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpConnectionManager;
+import org.apache.commons.httpclient.HttpState;
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.commons.json.JSONArray;
+import org.apache.sling.commons.json.JSONException;
+import org.apache.sling.commons.json.JSONObject;
+import org.apache.sling.commons.json.JSONTokener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.util.Iterator;
+
+/**
+ * Pipe outputing binding related to a json stream: either an object
+ */
+public class JsonPipe extends BasePipe {
+ private static Logger logger = LoggerFactory.getLogger(JsonPipe.class);
+ public static final String RESOURCE_TYPE = "slingPipes/json";
+
+ HttpClient client;
+
+ JSONArray array;
+ Object binding;
+ int index = -1;
+
+ public final String REMOTE_START = "http";
+
+ public JsonPipe(Plumber plumber, Resource resource) throws Exception {
+ super(plumber, resource);
+ configureHttpClient();
+ }
+
+ /**
+ * Configure http client
+ */
+ private void configureHttpClient(){
+ HttpConnectionManager manager = new MultiThreadedHttpConnectionManager();
+ HttpConnectionManagerParams params = new HttpConnectionManagerParams();
+ manager.setParams(params);
+ client = new HttpClient(manager);
+ client.getParams().setAuthenticationPreemptive(false);
+ }
+
+ @Override
+ public Object getOutputBinding() {
+ return binding;
+ }
+
+ /**
+ * Retrieve remote JSON String, or null if any problem occurs
+ * @return
+ */
+ private String retrieveJSONString() {
+ String json = null;
+ String expression = getExpr();
+ if (expression.startsWith(REMOTE_START)){
+ GetMethod method = null;
+ HttpState httpState = new HttpState();
+ InputStream responseInputStream = null;
+ try {
+ String url = getExpr();
+ if (StringUtils.isNotBlank(url)) {
+ method = new GetMethod(url);
+ logger.debug("Executing GET {}", url);
+ int status = client.executeMethod(null, method, httpState);
+ if (status == HttpStatus.SC_OK) {
+ logger.debug("200 received, streaming content");
+ responseInputStream = method.getResponseBodyAsStream();
+ StringWriter writer = new StringWriter();
+ IOUtils.copy(responseInputStream, writer, "utf-8");
+ json = writer.toString();
+ }
+ }
+ }
+ catch(Exception e){
+ logger.error("unable to retrieve the data", e);
+ }finally{
+ if (method != null) {
+ method.releaseConnection();
+ }
+ IOUtils.closeQuietly(responseInputStream);
+ }
+ } else {
+ //other try: given expression *is* json
+ json = expression;
+ }
+ return json;
+ }
+
+
+ /**
+ * in case there is no successful retrieval of some JSON data, we cut the pipe here
+ * @return
+ */
+ public Iterator<Resource> getOutput() {
+ Iterator<Resource> output = EMPTY_ITERATOR;
+ binding = null;
+ String jsonString = retrieveJSONString();
+ if (StringUtils.isNotBlank(jsonString)){
+ try {
+ JSONTokener tokener = new JSONTokener(jsonString);
+ char firstChar = tokener.next();
+ if (firstChar == '[') {
+ binding = array = new JSONArray(jsonString);
+ index = 0;
+ output = new Iterator<Resource>() {
+ @Override
+ public boolean hasNext() {
+ return index < array.length();
+ }
+
+ @Override
+ public Resource next() {
+ try {
+ binding = array.get(index);
+ } catch(Exception e){
+ logger.error("Unable to retrieve {}nth item of jsonarray", index, e);
+ }
+ index++;
+ return getInput();
+ }
+ };
+ } else if (firstChar == '{') {
+ binding = new JSONObject(jsonString);
+ output = super.getOutput();
+ } else {
+ //simple string
+ binding = jsonString;
+ output = super.getOutput();
+ }
+ } catch (JSONException e) {
+ logger.error("unable to parse JSON {} ", jsonString, e);
+ }
+ }
+ return output;
+ }
+}
Added: sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/MovePipe.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/MovePipe.java?rev=1718377&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/MovePipe.java (added)
+++ sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/MovePipe.java Mon Dec 7 14:19:52 2015
@@ -0,0 +1,89 @@
+/*
+ * 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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import java.util.Collections;
+import java.util.Iterator;
+
+/**
+ * Does a JCR Move of a node, returns the resource corresponding to the moved node
+ */
+public class MovePipe extends BasePipe {
+ Logger logger = LoggerFactory.getLogger(MovePipe.class);
+
+ public static final String RESOURCE_TYPE = "slingPipes/mv";
+
+ public MovePipe(Plumber plumber, Resource resource) throws Exception {
+ super(plumber, resource);
+ }
+
+ @Override
+ public boolean modifiesContent() {
+ return true;
+ }
+
+ @Override
+ public Iterator<Resource> getOutput() {
+ Iterator<Resource> output = Collections.emptyIterator();
+ Resource resource = getInput();
+ if (resource != null && resource.adaptTo(Item.class) != null) {
+ String targetPath = getExpr();
+ try {
+ Session session = resolver.adaptTo(Session.class);
+ if (session.itemExists(targetPath)){
+ logger.warn("{} already exists, nothing will be done here, nothing outputed");
+ } else {
+ logger.info("moving resource {} to {}", resource.getPath(), targetPath);
+ if (!isDryRun()) {
+ if (resource.adaptTo(Node.class) != null) {
+ session.move(resource.getPath(), targetPath);
+ } else {
+ int lastLevel = targetPath.lastIndexOf("/");
+ // /a/b/c will get cut in /a/b for parent path, and c for name
+ String parentPath = targetPath.substring(0, lastLevel);
+ String name = targetPath.substring(lastLevel + 1, targetPath.length());
+ Property sourceProperty = resource.adaptTo(Property.class);
+ Node destNode = session.getNode(parentPath);
+ if (sourceProperty.isMultiple()){
+ destNode.setProperty(name, sourceProperty.getValues(), sourceProperty.getType());
+ } else {
+ destNode.setProperty(name, sourceProperty.getValue(), sourceProperty.getType());
+ }
+ sourceProperty.remove();
+ }
+ Resource target = resolver.getResource(targetPath);
+ output = Collections.singleton(target).iterator();
+ }
+ }
+ } catch (RepositoryException e){
+ logger.error("unable to move the resource", e);
+ }
+ } else {
+ logger.warn("bad configuration of the pipe, will do nothing");
+ }
+ return output;
+ }
+}
Added: sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/MultiPropertyPipe.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/MultiPropertyPipe.java?rev=1718377&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/MultiPropertyPipe.java (added)
+++ sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/MultiPropertyPipe.java Mon Dec 7 14:19:52 2015
@@ -0,0 +1,114 @@
+/*
+ * 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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.Value;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+
+/**
+ * reads input MV property, outputs N times the input parent node resource, where N is the number of
+ * values in the property, outputs each value in the bindings
+ */
+public class MultiPropertyPipe extends BasePipe {
+ private static Logger logger = LoggerFactory.getLogger(MultiPropertyPipe.class);
+ public static final String RESOURCE_TYPE = "slingPipes/multiProperty";
+
+ public MultiPropertyPipe(Plumber plumber, Resource resource) throws Exception {
+ super(plumber, resource);
+ }
+
+ MVResourceIterator iterator;
+
+ @Override
+ public Iterator<Resource> getOutput() {
+ iterator = new MVResourceIterator(getInput());
+ return iterator;
+ }
+
+ @Override
+ public Object getOutputBinding() {
+ if (iterator != null) {
+ Value value = iterator.getCurrentValue();
+ try {
+ switch (value.getType()) {
+ case PropertyType.STRING: {
+ return value.getString();
+ }
+ default: {
+ return value.toString();
+ }
+ }
+ } catch (Exception e) {
+ logger.error("current value format is not supported", e);
+ }
+ return value.toString();
+ }
+ return null;
+ }
+
+ static class MVResourceIterator implements Iterator<Resource> {
+ Resource resource;
+ Value currentValue;
+ Iterator<Value> itValue = Collections.emptyIterator();
+
+ public MVResourceIterator(Resource resource){
+ this.resource = resource;
+ try {
+ Property mvProperty = resource.adaptTo(Property.class);
+ if (mvProperty == null) {
+ throw new Exception("input resource " + resource.getPath() + " is supposed to be a property");
+ }
+ if (!mvProperty.isMultiple()) {
+ throw new Exception("given property " + resource.getPath() + " is supposed to be multiple");
+ }
+ itValue = Arrays.asList(mvProperty.getValues()).iterator();
+ } catch (Exception e) {
+ logger.warn("unable to setup mv iterator for resource, will return nothing", e);
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ return itValue.hasNext();
+ }
+
+ public Value getCurrentValue() {
+ return currentValue;
+ }
+
+ @Override
+ public Resource next() {
+ if (itValue.hasNext()) {
+ currentValue = itValue.next();
+ }
+ return resource;
+ }
+
+ @Override
+ public void remove() {
+
+ }
+ }
+}
Added: sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/ParentPipe.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/ParentPipe.java?rev=1718377&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/ParentPipe.java (added)
+++ sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/ParentPipe.java Mon Dec 7 14:19:52 2015
@@ -0,0 +1,40 @@
+/*
+ * 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.Collections;
+import java.util.Iterator;
+
+/**
+ * very simple pipe, returning parent resource of input resource
+ */
+public class ParentPipe extends BasePipe {
+
+ public static final String RESOURCE_TYPE = "slingPipes/parent";
+
+ public ParentPipe(Plumber plumber, Resource resource) throws Exception {
+ super(plumber, resource);
+ }
+
+ @Override
+ public Iterator<Resource> getOutput() {
+ Resource resource = getInput();
+ return Collections.singleton(resource.getParent()).iterator();
+ }
+}
Added: sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/PathPipe.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/PathPipe.java?rev=1718377&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/PathPipe.java (added)
+++ sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/PathPipe.java Mon Dec 7 14:19:52 2015
@@ -0,0 +1,94 @@
+/*
+ * 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 javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+
+/**
+ * creates or get given expression's path and returns corresponding resource
+ */
+public class PathPipe extends BasePipe {
+
+ public static final String RESOURCE_TYPE = "slingPipes/path";
+ public static final String PN_NODETYPE = "nodeType";
+ public static final String PN_AUTOSAVE = "autosave";
+
+ String nodeType;
+
+ boolean autosave;
+
+ public PathPipe(Plumber plumber, Resource resource) throws Exception {
+ super(plumber, resource);
+ nodeType = properties.get(PN_NODETYPE, "sling:Folder");
+ autosave = properties.get(PN_AUTOSAVE, true);
+ }
+
+ @Override
+ public Iterator<Resource> getOutput() {
+ Iterator<Resource> output = Collections.emptyIterator();
+ String expression = getExpr();
+ Node leaf = null;
+ boolean transientChange = false;
+ try {
+ String relativePath = expression.substring(1);
+ Node parentNode = resolver.adaptTo(Session.class).getRootNode();
+ if (!parentNode.hasNode(relativePath)) {
+ Node node = parentNode;
+ int pos = relativePath.lastIndexOf('/');
+ if (pos != -1) {
+ final StringTokenizer st = new StringTokenizer(relativePath.substring(0, pos), "/");
+ while (st.hasMoreTokens()) {
+ final String token = st.nextToken();
+ if (!node.hasNode(token)) {
+ try {
+ node.addNode(token, nodeType);
+ transientChange = true;
+ } catch (RepositoryException re) {
+ // we ignore this as this folder might be created from a different task
+ node.refresh(false);
+ }
+ }
+ node = node.getNode(token);
+ }
+ relativePath = relativePath.substring(pos + 1);
+ }
+ if (!node.hasNode(relativePath)) {
+ node.addNode(relativePath, nodeType);
+ transientChange = true;
+ }
+ leaf = node.getNode(relativePath);
+ }
+ if (leaf == null) {
+ leaf = parentNode.getNode(relativePath);
+ }
+ if (transientChange && autosave){
+ resolver.adaptTo(Session.class).save();
+ }
+ output = Collections.singleton(resolver.getResource(leaf.getPath())).iterator();
+ } catch (RepositoryException e){
+ logger.error ("Not able to create path {}", expression, e);
+ }
+ return output;
+ }
+}
Added: sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/Pipe.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/Pipe.java?rev=1718377&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/Pipe.java (added)
+++ sling/trunk/contrib/extensions/sling-pipes/src/main/java/org/apache/sling/pipes/Pipe.java Mon Dec 7 14:19:52 2015
@@ -0,0 +1,118 @@
+/*
+ * 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.Iterator;
+
+/**
+ * Pipe interface
+ */
+public interface Pipe {
+ /**
+ * Name of the pipe
+ */
+ public static final String PN_NAME = "name";
+
+ /**
+ * expression of the pipe, usage depends on the pipe implementation
+ */
+ public static final String PN_EXPR = "expr";
+
+ /**
+ * resource's path associated to the path, usage depends on the pipe implementation
+ */
+ public static final String PN_PATH = "path";
+
+ /**
+ * Node name for the pipe's configuration
+ */
+ public static final String NN_CONF = "conf";
+
+ public static final String PN_DISTRIBUTION_AGENT = "distribution.agent";
+
+ /**
+ * returns true if that pipe will modify content during its execution
+ * @return
+ */
+ boolean modifiesContent();
+
+ /**
+ * returns true if that pipe is set not to write content
+ * @return
+ */
+ boolean isDryRun();
+
+ /**
+ * Return the name of that pipe
+ * @return
+ */
+ String getName();
+
+ /**
+ * Set parent
+ */
+ void setParent(ContainerPipe parent);
+
+ /**
+ * Return parent's pipe (can be null)
+ * @return
+ */
+ ContainerPipe getParent();
+
+ /**
+ * Get the pipe's optional configured resource or null
+ * @return
+ */
+ Resource getConfiguredInput();
+
+ /**
+ * Get pipe current's resource *before* next execution, meaning either the
+ * configured resource, either previous' pipe output resource
+ * @return
+ */
+ Resource getInput();
+
+ /**
+ * returns the binding output used in container pipe's expression
+ * @return
+ */
+ Object getOutputBinding();
+
+ /**
+ * returns the pipe's bindings
+ * @return
+ */
+ PipeBindings getBindings();
+
+ /**
+ * set the pipe's bindings
+ */
+ void setBindings(PipeBindings bindings);
+
+ /**
+ * Executes the pipe, can be contained in a parent or not
+ * @return
+ */
+ Iterator<Resource> getOutput();
+
+ /**
+ * Get Distribution agent
+ */
+ String getDistributionAgent();
+}
\ No newline at end of file