http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/NodeBuilderBaseImpl.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/NodeBuilderBaseImpl.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/NodeBuilderBaseImpl.java
new file mode 100644
index 0000000..a4d37bb
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/NodeBuilderBaseImpl.java
@@ -0,0 +1,340 @@
+/**
+ * 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.oozie.fluentjob.api.action;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.oozie.fluentjob.api.Condition;
+import org.apache.oozie.fluentjob.api.ModifyOnce;
+import org.apache.oozie.fluentjob.api.workflow.Credential;
+
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * An abstract base class for builders that build concrete instances of 
subclasses of {@link Node}. This class doesn't
+ * implement the {@link Builder} interface as no type information as to the 
concrete node to build. The concrete node
+ * builder classes of course should implement {@link Builder}.
+ *
+ * The concrete builders should provide a fluent API, and to facilitate this, 
the methods in this base class have to
+ * return the concrete builder. Therefore it is templated on the type of the 
concrete builder class. Although it cannot
+ * be enforced that the provided generic parameter is the same as the class 
deriving from this class, it definitely
+ * should be, and the constraint on the type parameter tries to minimize the 
chance that the class is subclassed
+ * incorrectly.
+ *
+ * The properties of the builder can only be set once, an attempt to set them 
a second time will trigger
+ * an {@link IllegalStateException}. The properties that are lists are an 
exception to this rule, of course multiple
+ * elements can be added / removed.
+ *
+ * @param <B> The type of the concrete builder class deriving from this class.
+ */
[email protected]
[email protected]
+public abstract class NodeBuilderBaseImpl <B extends NodeBuilderBaseImpl<B>> {
+    private final ModifyOnce<String> name;
+    private final List<Credential> credentials;
+    private final ModifyOnce<Integer> retryMax;
+    private final ModifyOnce<Integer> retryInterval;
+    private final ModifyOnce<String> retryPolicy;
+    private final List<Node> parents;
+    private final List<Node.NodeWithCondition> parentsWithConditions;
+
+    private final ModifyOnce<ErrorHandler> errorHandler;
+
+    NodeBuilderBaseImpl() {
+        this(null);
+    }
+
+    NodeBuilderBaseImpl(final Node node) {
+        if (node == null) {
+            name = new ModifyOnce<>();
+            credentials = new ArrayList<>();
+            retryMax = new ModifyOnce<>();
+            retryInterval = new ModifyOnce<>();
+            retryPolicy = new ModifyOnce<>();
+            parents = new ArrayList<>();
+            parentsWithConditions = new ArrayList<>();
+            errorHandler = new ModifyOnce<>();
+        }
+        else {
+            // Names won't be copied as we need unique names within a workflow
+            name = new ModifyOnce<>();
+            credentials = new ArrayList<>(node.getCredentials());
+            retryMax = new ModifyOnce<>(node.getRetryMax());
+            retryInterval = new ModifyOnce<>(node.getRetryInterval());
+            retryPolicy = new ModifyOnce<>(node.getRetryPolicy());
+            parents = new ArrayList<>(node.getParentsWithoutConditions());
+            parentsWithConditions = new 
ArrayList<>(node.getParentsWithConditions());
+            errorHandler = new ModifyOnce<>(node.getErrorHandler());
+        }
+    }
+
+    /**
+     * Registers an error handler with this builder.
+     * @param errorHandler The error handler to register.
+     * @return This builder.
+     */
+    public B withErrorHandler(final ErrorHandler errorHandler) {
+        this.errorHandler.set(errorHandler);
+        return ensureRuntimeSelfReference();
+    }
+
+    /**
+     * Removes the currently registered error handler if any.
+     * @return This builder.
+     */
+    public B withoutErrorHandler() {
+        errorHandler.set(null);
+        return ensureRuntimeSelfReference();
+    }
+
+    /**
+     * Registers a name that will be the name of the action built by this 
builder.
+     * @param name The name of the action that will be built.
+     * @return This builder.
+     */
+    public B withName(final String name) {
+        this.name.set(name);
+        return ensureRuntimeSelfReference();
+    }
+
+    /**
+     * Registers a {@link Credential} that will be the {@code cred} of the 
action built by this builder.
+     * @param credential The {@link Credential} of the action that will be 
built.
+     * @return This builder.
+     */
+    public B withCredential(final Credential credential) {
+        this.credentials.add(credential);
+        return ensureRuntimeSelfReference();
+    }
+
+    /**
+     * Removes a {@link Credential} registered with this builder. If the 
{@code credential} is not registered with this builder,
+     * this method does nothing.
+     * @param credential The {@link Credential} to remove.
+     * @return This builder.
+     */
+    public B withoutCredential(final Credential credential) {
+        this.credentials.remove(credential);
+        return ensureRuntimeSelfReference();
+    }
+
+    /**
+     * Removes all {@link Credential}s registered with this builder.
+     * @return This builder.
+     */
+    public B clearCredentials() {
+        this.credentials.clear();
+        return ensureRuntimeSelfReference();
+    }
+
+    /**
+     * Registers an {@link Integer} that will be the {@code retry-max} of the 
action built by this builder.
+     * @param retryMax The {@code retry-max} of the action that will be built.
+     * @return This builder.
+     */
+    public B withRetryMax(final Integer retryMax) {
+        this.retryMax.set(retryMax);
+        return ensureRuntimeSelfReference();
+    }
+
+    /**
+     * Registers an {@link Integer} that will be the {@code retry-interval} of 
the action built by this builder.
+     * @param retryInterval The {@code retry-interval} of the action that will 
be built.
+     * @return This builder.
+     */
+    public B withRetryInterval(final Integer retryInterval) {
+        this.retryInterval.set(retryInterval);
+        return ensureRuntimeSelfReference();
+    }
+
+    /**
+     * Registers a {@link String} that will be the {@code retry-policy} of the 
action built by this builder.
+     * @param retryPolicy The {@code retry-policy} of the action that will be 
built.
+     * @return This builder.
+     */
+    public B withRetryPolicy(final String retryPolicy) {
+        this.retryPolicy.set(retryPolicy);
+        return ensureRuntimeSelfReference();
+    }
+
+    /**
+     * Registers an unconditional parent with this builder. If the parent is 
already registered with this builder,
+     * {@link IllegalArgumentException} is thrown.
+     * @param parent The node that will be the parent of the built action.
+     * @return This builder.
+     *
+     * @throws IllegalArgumentException if the provided node is already 
registered as a parent.
+     */
+    public B withParent(final Node parent) {
+        checkNoDuplicateParent(parent);
+
+        parents.add(parent);
+        return ensureRuntimeSelfReference();
+    }
+
+    /**
+     * Registers a conditional parent with this builder. If the parent is 
already registered with this builder,
+     * {@link IllegalArgumentException} is thrown.
+     * @param parent The node that will be the parent of the built action.
+     * @param condition The condition of the parent.
+     * @return This builder.
+     *
+     * @throws IllegalArgumentException if the provided node is already 
registered as a parent.
+     */
+    public B withParentWithCondition(final Node parent, final String 
condition) {
+        checkNoDuplicateParent(parent);
+
+        parentsWithConditions.add(new Node.NodeWithCondition(parent, 
Condition.actualCondition(condition)));
+        return ensureRuntimeSelfReference();
+    }
+
+    /**
+     * Registers a conditional parent for which this node is the default 
transition. If the parent is already registered
+     * with this builder, {@link IllegalArgumentException} is thrown.
+     * {@link IllegalArgumentException} is thrown.
+     * @param parent The node that will be the parent of the built action.
+     * @return This builder.
+     *
+     * @throws IllegalArgumentException if the provided node is already 
registered as a parent.
+     */
+    public B withParentDefaultConditional(final Node parent) {
+        checkNoDuplicateParent(parent);
+
+        parentsWithConditions.add(new Node.NodeWithCondition(parent, 
Condition.defaultCondition()));
+        return ensureRuntimeSelfReference();
+    }
+
+    /**
+     * Removes a parent registered with this builder. If the parent is not 
registered with this builder, this method
+     * does nothing.
+     * @param parent The parent to remove.
+     * @return This builder.
+     */
+    public B withoutParent(final Node parent) {
+        if (parents.contains(parent)) {
+            parents.remove(parent);
+        } else {
+            int index = indexOfParentAmongParentsWithConditions(parent);
+            if (index >= 0) {
+                parentsWithConditions.remove(index);
+            }
+        }
+
+        return ensureRuntimeSelfReference();
+    }
+
+    /**
+     * Removes all parents registered with this builder.
+     * @return This builder.
+     */
+    public B clearParents() {
+        parents.clear();
+        parentsWithConditions.clear();
+        return ensureRuntimeSelfReference();
+    }
+
+    final B ensureRuntimeSelfReference() {
+        final B runtimeSelfReference = getRuntimeSelfReference();
+
+        Preconditions.checkState(runtimeSelfReference == this, "The builder 
type B doesn't extend NodeBuilderBaseImpl<B>.");
+
+        return runtimeSelfReference;
+    }
+
+    private void checkNoDuplicateParent(final Node parent) {
+        boolean parentsContains = parents.contains(parent);
+        boolean parentsWithConditionsContains = 
indexOfParentAmongParentsWithConditions(parent) != -1;
+
+        Preconditions.checkArgument(!parentsContains && 
!parentsWithConditionsContains,
+                "Trying to add a parent that is already a parent of this 
node.");
+    }
+
+    private int indexOfParentAmongParentsWithConditions(final Node parent) {
+        for (int i = 0; i < parentsWithConditions.size(); ++i) {
+            if (parent == parentsWithConditions.get(i).getNode()) {
+                return i;
+            }
+        }
+
+        return -1;
+    }
+
+    protected void addAsChildToAllParents(final Node child) {
+        final List<Node> parentsList = child.getParentsWithoutConditions();
+        if (parentsList != null) {
+            for (final Node parent : parentsList) {
+                parent.addChild(child);
+            }
+        }
+
+        final List<Node.NodeWithCondition> parentsWithConditionsList = 
child.getParentsWithConditions();
+        if (parentsWithConditionsList != null) {
+            for (final Node.NodeWithCondition parentWithCondition : 
parentsWithConditionsList) {
+                final Node parent = parentWithCondition.getNode();
+                final Condition condition = parentWithCondition.getCondition();
+
+                if (condition.isDefault()) {
+                    parent.addChildAsDefaultConditional(child);
+                }
+                else {
+                    parent.addChildWithCondition(child, 
condition.getCondition());
+                }
+            }
+        }
+    }
+
+    Node.ConstructionData getConstructionData() {
+        final String nameStr = ensureName();
+
+        final ImmutableList<Node> parentsList = new 
ImmutableList.Builder<Node>().addAll(parents).build();
+        final ImmutableList<Node.NodeWithCondition> parentsWithConditionsList
+                = new 
ImmutableList.Builder<Node.NodeWithCondition>().addAll(parentsWithConditions).build();
+
+        return new Node.ConstructionData(
+                new Node.Attributes(nameStr,
+                        credentials,
+                        retryMax.get(),
+                        retryInterval.get(),
+                        retryPolicy.get()),
+                parentsList,
+                parentsWithConditionsList,
+                errorHandler.get()
+        );
+    }
+
+    private String ensureName() {
+        if (Strings.isNullOrEmpty(this.name.get())) {
+            final String type = 
getRuntimeSelfReference().getClass().getSimpleName()
+                    
.toLowerCase(Locale.getDefault()).replaceAll("actionbuilder", "");
+            final int randomSuffix = new SecureRandom().nextInt(1_000_000_000);
+
+            this.name.set(String.format("%s-%d", type, randomSuffix));
+        }
+
+        return this.name.get();
+    }
+
+    protected abstract B getRuntimeSelfReference();
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/PigAction.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/PigAction.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/PigAction.java
new file mode 100644
index 0000000..d77c539
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/PigAction.java
@@ -0,0 +1,107 @@
+/**
+ * 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.oozie.fluentjob.api.action;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class representing the Oozie Pig action.
+ * Instances of this class should be built using the builder {@link 
PigActionBuilder}.
+ *
+ * The properties of the builder can only be set once, an attempt to set them 
a second time will trigger
+ * an {@link IllegalStateException}.
+ *
+ * Builder instances can be used to build several elements, although 
properties already set cannot be changed after
+ * a call to {@link PigActionBuilder#build} either.
+ */
[email protected]
[email protected]
+public class PigAction extends Node implements HasAttributes {
+    private final ActionAttributes attributes;
+    private final String script;
+    private final ImmutableList<String> params;
+
+    PigAction(final ConstructionData constructionData,
+              final ActionAttributes attributes,
+              final String script,
+              final ImmutableList<String> params) {
+        super(constructionData);
+
+        this.attributes = attributes;
+        this.script = script;
+        this.params = params;
+    }
+
+    public String getResourceManager() {
+        return attributes.getResourceManager();
+    }
+
+    public String getNameNode() {
+        return attributes.getNameNode();
+    }
+
+    public Prepare getPrepare() {
+        return attributes.getPrepare();
+    }
+
+    public Launcher getLauncher() {
+        return attributes.getLauncher();
+    }
+
+    public List<String> getJobXmls() {
+        return attributes.getJobXmls();
+    }
+
+    public String getConfigProperty(final String property) {
+        return attributes.getConfiguration().get(property);
+    }
+
+    public Map<String, String> getConfiguration() {
+        return attributes.getConfiguration();
+    }
+
+    public String getScript() {
+        return script;
+    }
+
+    public List<String> getParams() {
+        return params;
+    }
+
+    public List<String> getArgs() {
+        return attributes.getArgs();
+    }
+
+    public List<String> getFiles() {
+        return attributes.getFiles();
+    }
+
+    public List<String> getArchives() {
+        return attributes.getArchives();
+    }
+
+    public ActionAttributes getAttributes() {
+        return attributes;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/PigActionBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/PigActionBuilder.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/PigActionBuilder.java
new file mode 100644
index 0000000..aa1ff16
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/PigActionBuilder.java
@@ -0,0 +1,215 @@
+/**
+ * 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.oozie.fluentjob.api.action;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.oozie.fluentjob.api.ModifyOnce;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A builder class for {@link PigAction}.
+ *
+ * The properties of the builder can only be set once, an attempt to set them 
a second time will trigger
+ * an {@link IllegalStateException}. The properties that are lists are an 
exception to this rule, of course multiple
+ * elements can be added / removed.
+ *
+ * Builder instances can be used to build several elements, although 
properties already set cannot be changed after
+ * a call to {@link PigActionBuilder#build} either.
+ */
[email protected]
[email protected]
+public class PigActionBuilder extends NodeBuilderBaseImpl<PigActionBuilder> 
implements Builder<PigAction> {
+    protected final ActionAttributesBuilder attributesBuilder;
+    protected final ModifyOnce<String> script;
+    protected final List<String> params;
+
+    public static PigActionBuilder create() {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.create();
+        final ModifyOnce<String> script = new ModifyOnce<>();
+        final List<String> params = new ArrayList<>();
+
+        return new PigActionBuilder(
+                null,
+                builder,
+                script,
+                params);
+    }
+
+    public static PigActionBuilder createFromExistingAction(final PigAction 
action) {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.createFromExisting(action.getAttributes());
+        final ModifyOnce<String> script = new ModifyOnce<>(action.getScript());
+        final List<String> params = new ArrayList<>(action.getParams());
+
+        return new PigActionBuilder(action,
+                builder,
+                script,
+                params);
+    }
+
+    public static PigActionBuilder createFromExistingAction(final Node action) 
{
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.createFromAction(action);
+        final ModifyOnce<String> script = new ModifyOnce<>();
+        final List<String> params = new ArrayList<>();
+
+        return new PigActionBuilder(action,
+                builder,
+                script,
+                params);
+    }
+
+    PigActionBuilder(final Node action,
+                     final ActionAttributesBuilder attributesBuilder,
+                     final ModifyOnce<String> script,
+                     final List<String> params) {
+        super(action);
+
+        this.attributesBuilder = attributesBuilder;
+        this.script = script;
+        this.params = params;
+    }
+
+    public PigActionBuilder withResourceManager(final String resourceManager) {
+        this.attributesBuilder.withResourceManager(resourceManager);
+        return this;
+    }
+
+    public PigActionBuilder withNameNode(final String nameNode) {
+        this.attributesBuilder.withNameNode(nameNode);
+        return this;
+    }
+
+    public PigActionBuilder withPrepare(final Prepare prepare) {
+        this.attributesBuilder.withPrepare(prepare);
+        return this;
+    }
+
+    public PigActionBuilder withLauncher(final Launcher launcher) {
+        this.attributesBuilder.withLauncher(launcher);
+        return this;
+    }
+
+    public PigActionBuilder withJobXml(final String jobXml) {
+        this.attributesBuilder.withJobXml(jobXml);
+        return this;
+    }
+
+    public PigActionBuilder withoutJobXml(final String jobXml) {
+        this.attributesBuilder.withoutJobXml(jobXml);
+        return this;
+    }
+
+    public PigActionBuilder clearJobXmls() {
+        this.attributesBuilder.clearJobXmls();
+        return this;
+    }
+
+    public PigActionBuilder withConfigProperty(final String key, final String 
value) {
+        this.attributesBuilder.withConfigProperty(key, value);
+        return this;
+    }
+
+    public PigActionBuilder withScript(final String script) {
+        this.script.set(script);
+        return this;
+    }
+
+    public PigActionBuilder withParam(final String param) {
+        this.params.add(param);
+        return this;
+    }
+
+    public PigActionBuilder withoutParam(final String param) {
+        this.params.remove(param);
+        return this;
+    }
+
+    public PigActionBuilder clearParams() {
+        this.params.clear();
+        return this;
+    }
+
+    public PigActionBuilder withArg(final String arg) {
+        this.attributesBuilder.withArg(arg);
+        return this;
+    }
+
+    public PigActionBuilder withoutArg(final String arg) {
+        this.attributesBuilder.withoutArg(arg);
+        return this;
+    }
+
+    public PigActionBuilder clearArgs() {
+        this.attributesBuilder.clearArgs();
+        return this;
+    }
+
+    public PigActionBuilder withFile(final String file) {
+        this.attributesBuilder.withFile(file);
+        return this;
+    }
+
+    public PigActionBuilder withoutFile(final String file) {
+        this.attributesBuilder.withoutFile(file);
+        return this;
+    }
+
+    public PigActionBuilder clearFiles() {
+        this.attributesBuilder.clearFiles();
+        return this;
+    }
+
+    public PigActionBuilder withArchive(final String archive) {
+        this.attributesBuilder.withArchive(archive);
+        return this;
+    }
+
+    public PigActionBuilder withoutArchive(final String archive) {
+        this.attributesBuilder.withoutArchive(archive);
+        return this;
+    }
+
+    public PigActionBuilder clearArchives() {
+        this.attributesBuilder.clearArchives();
+        return this;
+    }
+
+    @Override
+    public PigAction build() {
+        final Node.ConstructionData constructionData = getConstructionData();
+
+        final PigAction instance = new PigAction(
+                constructionData,
+                attributesBuilder.build(),
+                script.get(),
+                ImmutableList.copyOf(params));
+
+        addAsChildToAllParents(instance);
+
+        return instance;
+    }
+
+    @Override
+    protected PigActionBuilder getRuntimeSelfReference() {
+        return this;
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Pipes.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Pipes.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Pipes.java
new file mode 100644
index 0000000..526a202
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Pipes.java
@@ -0,0 +1,106 @@
+/**
+ * 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.oozie.fluentjob.api.action;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+/**
+ * A class representing the piping information within a {@link 
MapReduceAction}.
+ *
+ * Instances of this class should be built using the builder {@link 
PipesBuilder}.
+ *
+ * The properties of the builder can only be set once, an attempt to set them 
a second time will trigger
+ * an {@link IllegalStateException}.
+ *
+ * Builder instances can be used to build several elements, although 
properties already set cannot be changed after
+ * a call to {@link PipesBuilder#build} either.
+ */
[email protected]
[email protected]
+public class Pipes {
+    private final String map;
+    private final String reduce;
+    private final String inputformat;
+    private final String partitioner;
+    private final String writer;
+    private final String program;
+
+    Pipes(final String map,
+          final String reduce,
+          final String inputformat,
+          final String partitioner,
+          final String writer,
+          final String program) {
+        this.map = map;
+        this.reduce = reduce;
+        this.inputformat = inputformat;
+        this.partitioner = partitioner;
+        this.writer = writer;
+        this.program = program;
+    }
+
+    /**
+     * Returns the mapper of this {@link Pipes} object.
+     * @return The mapper of this {@link Pipes} object.
+     */
+    public String getMap() {
+        return map;
+    }
+
+    /**
+     * Returns the reducer of this {@link Pipes} object.
+     * @return The reducer of this {@link Pipes} object.
+     */
+    public String getReduce() {
+        return reduce;
+    }
+
+    /**
+     * Returns the input format of this {@link Pipes} object.
+     * @return The input format of this {@link Pipes} object.
+     */
+    public String getInputformat() {
+        return inputformat;
+    }
+
+    /**
+     * Returns the partitioner of this {@link Pipes} object.
+     * @return The partitioner of this {@link Pipes} object.
+     */
+    public String getPartitioner() {
+        return partitioner;
+    }
+
+    /**
+     * Returns the writer of this {@link Pipes} object.
+     * @return The writer of this {@link Pipes} object.
+     */
+    public String getWriter() {
+        return writer;
+    }
+
+    /**
+     * Returns the program of this {@link Pipes} object.
+     * @return The program of this {@link Pipes} object.
+     */
+    public String getProgram() {
+        return program;
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/PipesBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/PipesBuilder.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/PipesBuilder.java
new file mode 100644
index 0000000..0699dba
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/PipesBuilder.java
@@ -0,0 +1,127 @@
+/**
+ * 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.oozie.fluentjob.api.action;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.oozie.fluentjob.api.ModifyOnce;
+
+/**
+ * A builder class for {@link Pipes}.
+ *
+ * The properties of the builder can only be set once, an attempt to set them 
a second time will trigger
+ * an {@link IllegalStateException}. The properties that are lists are an 
exception to this rule, of course multiple
+ * elements can be added / removed.
+ *
+ * Builder instances can be used to build several elements, although 
properties already set cannot be changed after
+ * a call to {@link PipesBuilder#build} either.
+ */
[email protected]
[email protected]
+public class PipesBuilder implements Builder<Pipes> {
+    private final ModifyOnce<String> map;
+    private final ModifyOnce<String> reduce;
+    private final ModifyOnce<String> inputformat;
+    private final ModifyOnce<String> partitioner;
+    private final ModifyOnce<String> writer;
+    private final ModifyOnce<String> program;
+
+    /**
+     * Creates a new {@link PipesBuilder}.
+     */
+    public PipesBuilder() {
+        map = new ModifyOnce<>();
+        reduce = new ModifyOnce<>();
+        inputformat = new ModifyOnce<>();
+        partitioner = new ModifyOnce<>();
+        writer = new ModifyOnce<>();
+        program = new ModifyOnce<>();
+    }
+
+    /**
+     * Registers a mapper with this builder.
+     * @param map The mapper to register with this builder.
+     * @return This builder.
+     */
+    public PipesBuilder withMap(final String map) {
+        this.map.set(map);
+        return this;
+    }
+
+    /**
+     * Registers a reducer with this builder.
+     * @param reduce The reducer to register with this builder.
+     * @return This builder.
+     */
+    public PipesBuilder withReduce(final String reduce) {
+        this.reduce.set(reduce);
+        return this;
+    }
+
+    /**
+     * Registers an input format with this builder.
+     * @param inputformat The input format to register with this builder.
+     * @return This builder.
+     */
+    public PipesBuilder withInputformat(final String inputformat) {
+        this.inputformat.set(inputformat);
+        return this;
+    }
+
+    /**
+     * Registers a partitioner with this builder.
+     * @param partitioner The partitioner to register with this builder.
+     * @return This builder.
+     */
+    public PipesBuilder withPartitioner(final String partitioner) {
+        this.partitioner.set(partitioner);
+        return this;
+    }
+
+    /**
+     * Registers a writer with this builder.
+     * @param writer The writer to register with this builder.
+     * @return This builder.
+     */
+    public PipesBuilder withWriter(final String writer) {
+        this.writer.set(writer);
+        return this;
+    }
+
+    /**
+     * Registers an executable program with this builder.
+     * @param program The executable program to register with this builder.
+     * @return This builder.
+     */
+    public PipesBuilder withProgram(final String program) {
+        this.program.set(program);
+        return this;
+    }
+
+    /**
+     * Creates a new {@link Pipes} object with the properties stores in this 
builder.
+     * The new {@link Pipes} object is independent of this builder and the 
builder can be used to build
+     * new instances.
+     * @return A new {@link Pipes} object with the properties stored in this 
builder.
+     */
+    @Override
+    public Pipes build() {
+        return new Pipes(map.get(), reduce.get(), inputformat.get(), 
partitioner.get(), writer.get(), program.get());
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Prepare.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Prepare.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Prepare.java
new file mode 100644
index 0000000..2d121a2
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Prepare.java
@@ -0,0 +1,58 @@
+/**
+ * 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.oozie.fluentjob.api.action;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+import java.util.List;
+
+/**
+ * A class representing the prepare section of various actions.
+ *
+ * Instances of this class should be built using the builder {@link 
PrepareBuilder}.
+ */
[email protected]
[email protected]
+public class Prepare {
+    private final ImmutableList<Delete> deletes;
+    private final ImmutableList<Mkdir> mkdirs;
+
+    Prepare(final ImmutableList<Delete> deletes, final ImmutableList<Mkdir> 
mkdirs) {
+        this.deletes = deletes;
+        this.mkdirs = mkdirs;
+    }
+
+    /**
+     * Returns the {@link Delete} objects that specify which directories or 
files will be deleted.
+     * @return The {@link Delete} objects that specify which directories or 
files will be deleted.
+     */
+    public List<Delete> getDeletes() {
+        return deletes;
+    }
+
+    /**
+     * Returns the {@link Mkdir} objects that specify which directories will 
be created.
+     * @return The {@link Mkdir} objects that specify which directories will 
be created.
+     */
+    public List<Mkdir> getMkdirs() {
+        return mkdirs;
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/PrepareBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/PrepareBuilder.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/PrepareBuilder.java
new file mode 100644
index 0000000..22042e9
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/PrepareBuilder.java
@@ -0,0 +1,85 @@
+/**
+ * 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.oozie.fluentjob.api.action;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+/**
+ * A builder class for {@link Prepare}.
+ * <p>
+ * Builder instances can be used to build several elements, although 
properties already set cannot be changed after
+ * a call to {@link FSActionBuilder#build} either.
+ */
[email protected]
[email protected]
+public class PrepareBuilder implements Builder<Prepare> {
+    private final ImmutableList.Builder<Delete> deletes;
+    private final ImmutableList.Builder<Mkdir> mkdirs;
+
+    public PrepareBuilder() {
+        deletes = new ImmutableList.Builder<>();
+        mkdirs = new ImmutableList.Builder<>();
+    }
+
+    /**
+     * Registers a {@link Delete} object with this builder. The {@link Delete} 
object will have the provided path as
+     * its target and the default value (true) for skip-trash.
+     * @param path The target of the {@link Delete} object.
+     * @return this
+     */
+    public PrepareBuilder withDelete(final String path) {
+        return withDelete(path, null);
+    }
+
+    /**
+     * Registers a {@link Delete} object with this builder. The {@link Delete} 
object will have the provided path as
+     * its target and the given boolean value for skip-trash.
+     * @param path The target of the {@link Delete} object.
+     * @param skipTrash Whether to skip trash when deleting the items.
+     * @return this
+     */
+    public PrepareBuilder withDelete(final String path, final Boolean 
skipTrash) {
+        deletes.add(new Delete(path, skipTrash));
+        return this;
+    }
+
+    /**
+     * Registers a {@link Mkdir} object with this builder The {@link Mkdir} 
object will have the provided path as
+     * its target.
+     * @param path The target of the {@link Mkdir}.
+     * @return this
+     */
+    public PrepareBuilder withMkdir(final String path) {
+        mkdirs.add(new Mkdir(path));
+        return this;
+    }
+
+    /**
+     * Creates a new {@link Prepare} object with the properties stores in this 
builder.
+     * The new {@link Prepare} object is independent of this builder and the 
builder can be used to build
+     * new instances.
+     * @return A new {@link Prepare} object with the properties stored in this 
builder.
+     */
+    @Override
+    public Prepare build() {
+        return new Prepare(deletes.build(), mkdirs.build());
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/ShellAction.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/ShellAction.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/ShellAction.java
new file mode 100644
index 0000000..c1b79aa
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/ShellAction.java
@@ -0,0 +1,111 @@
+/**
+ * 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.oozie.fluentjob.api.action;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class representing the Oozie shell action.
+ * Instances of this class should be built using the builder {@link 
EmailActionBuilder}.
+ *
+ * The properties of the builder can only be set once, an attempt to set them 
a second time will trigger
+ * an {@link IllegalStateException}.
+ *
+ * Builder instances can be used to build several elements, although 
properties already set cannot be changed after
+ * a call to {@link ShellActionBuilder#build} either.
+ */
[email protected]
[email protected]
+public class ShellAction extends Node implements HasAttributes {
+    private final ActionAttributes attributes;
+    private final String executable;
+    private final ImmutableList<String> environmentVariables;
+
+    ShellAction(final ConstructionData constructionData,
+                final ActionAttributes attributes,
+                final String executable,
+                final ImmutableList<String> environmentVariables) {
+        super(constructionData);
+
+        this.attributes = attributes;
+        this.executable = executable;
+        this.environmentVariables = environmentVariables;
+    }
+
+    public String getResourceManager() {
+        return attributes.getResourceManager();
+    }
+
+    public String getNameNode() {
+        return attributes.getNameNode();
+    }
+
+    public Prepare getPrepare() {
+        return attributes.getPrepare();
+    }
+
+    public Launcher getLauncher() {
+        return attributes.getLauncher();
+    }
+
+    public List<String> getJobXmls() {
+        return attributes.getJobXmls();
+    }
+
+    public String getConfigProperty(final String property) {
+        return attributes.getConfiguration().get(property);
+    }
+
+    public Map<String, String> getConfiguration() {
+        return attributes.getConfiguration();
+    }
+
+    public String getExecutable() {
+        return executable;
+    }
+
+    public List<String> getArguments() {
+        return attributes.getArgs();
+    }
+
+    public List<String> getEnvironmentVariables() {
+        return environmentVariables;
+    }
+
+    public List<String> getFiles() {
+        return attributes.getFiles();
+    }
+
+    public List<String> getArchives() {
+        return attributes.getArchives();
+    }
+
+    public boolean isCaptureOutput() {
+        return attributes.isCaptureOutput();
+    }
+
+    public ActionAttributes getAttributes() {
+        return attributes;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/ShellActionBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/ShellActionBuilder.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/ShellActionBuilder.java
new file mode 100644
index 0000000..21b4192
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/ShellActionBuilder.java
@@ -0,0 +1,220 @@
+/**
+ * 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.oozie.fluentjob.api.action;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.oozie.fluentjob.api.ModifyOnce;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A builder class for {@link ShellAction}.
+ *
+ * The properties of the builder can only be set once, an attempt to set them 
a second time will trigger
+ * an {@link IllegalStateException}. The properties that are lists are an 
exception to this rule, of course multiple
+ * elements can be added / removed.
+ *
+ * Builder instances can be used to build several elements, although 
properties already set cannot be changed after
+ * a call to {@link ShellActionBuilder#build} either.
+ */
[email protected]
[email protected]
+public class ShellActionBuilder extends 
NodeBuilderBaseImpl<ShellActionBuilder> implements Builder<ShellAction> {
+    private final ActionAttributesBuilder attributesBuilder;
+    private final ModifyOnce<String> executable;
+    private final List<String> environmentVariables;
+
+    public static ShellActionBuilder create() {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.create();
+        final ModifyOnce<String> executable = new ModifyOnce<>();
+        final List<String> environmentVariables = new ArrayList<>();
+
+        return new ShellActionBuilder(
+                null,
+                builder,
+                executable,
+                environmentVariables);
+    }
+
+    public static ShellActionBuilder createFromExistingAction(final 
ShellAction action) {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.createFromExisting(action.getAttributes());
+        final ModifyOnce<String> executable = new 
ModifyOnce<>(action.getExecutable());
+        final List<String> environmentVariables = new 
ArrayList<>(action.getEnvironmentVariables());
+
+        return new ShellActionBuilder(action,
+                builder,
+                executable,
+                environmentVariables);
+    }
+
+    public static ShellActionBuilder createFromExistingAction(final Node 
action) {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.createFromAction(action);
+        final ModifyOnce<String> executable = new ModifyOnce<>();
+        final List<String> environmentVariables = new ArrayList<>();
+
+        return new ShellActionBuilder(action,
+                builder,
+                executable,
+                environmentVariables);
+    }
+
+    private ShellActionBuilder(final Node action,
+                               final ActionAttributesBuilder attributesBuilder,
+                               final ModifyOnce<String> executable,
+                               final List<String> environmentVariables) {
+        super(action);
+
+        this.attributesBuilder = attributesBuilder;
+        this.executable = executable;
+        this.environmentVariables = environmentVariables;
+    }
+
+    public ShellActionBuilder withResourceManager(final String 
resourceManager) {
+        this.attributesBuilder.withResourceManager(resourceManager);
+        return this;
+    }
+
+    public ShellActionBuilder withNameNode(final String nameNode) {
+        this.attributesBuilder.withNameNode(nameNode);
+        return this;
+    }
+
+    public ShellActionBuilder withPrepare(final Prepare prepare) {
+        this.attributesBuilder.withPrepare(prepare);
+        return this;
+    }
+
+    public ShellActionBuilder withLauncher(final Launcher launcher) {
+        this.attributesBuilder.withLauncher(launcher);
+        return this;
+    }
+
+    public ShellActionBuilder withJobXml(final String jobXml) {
+        this.attributesBuilder.withJobXml(jobXml);
+        return this;
+    }
+
+    public ShellActionBuilder withoutJobXml(final String jobXml) {
+        this.attributesBuilder.withoutJobXml(jobXml);
+        return this;
+    }
+
+    public ShellActionBuilder clearJobXmls() {
+        this.attributesBuilder.clearJobXmls();
+        return this;
+    }
+
+    public ShellActionBuilder withConfigProperty(final String key, final 
String value) {
+        this.attributesBuilder.withConfigProperty(key, value);
+        return this;
+    }
+
+    public ShellActionBuilder withExecutable(final String executable) {
+        this.executable.set(executable);
+        return this;
+    }
+
+    public ShellActionBuilder withArgument(final String argument) {
+        this.attributesBuilder.withArg(argument);
+        return this;
+    }
+
+    public ShellActionBuilder withoutArgument(final String argument) {
+        this.attributesBuilder.withoutArg(argument);
+        return this;
+    }
+
+    public ShellActionBuilder clearArguments() {
+        this.attributesBuilder.clearArgs();
+        return this;
+    }
+
+    public ShellActionBuilder withEnvironmentVariable(final String 
environmentVariable) {
+        this.environmentVariables.add(environmentVariable);
+        return this;
+    }
+
+    public ShellActionBuilder withoutEnvironmentVariable(final String 
environmentVariable) {
+        this.environmentVariables.remove(environmentVariable);
+        return this;
+    }
+
+    public ShellActionBuilder clearEnvironmentVariables() {
+        this.environmentVariables.clear();
+        return this;
+    }
+
+    public ShellActionBuilder withFile(final String file) {
+        this.attributesBuilder.withFile(file);
+        return this;
+    }
+
+    public ShellActionBuilder withoutFile(final String file) {
+        this.attributesBuilder.withoutFile(file);
+        return this;
+    }
+
+    public ShellActionBuilder clearFiles() {
+        this.attributesBuilder.clearFiles();
+        return this;
+    }
+
+    public ShellActionBuilder withArchive(final String archive) {
+        this.attributesBuilder.withArchive(archive);
+        return this;
+    }
+
+    public ShellActionBuilder withoutArchive(final String archive) {
+        this.attributesBuilder.withoutArchive(archive);
+        return this;
+    }
+
+    public ShellActionBuilder clearArchives() {
+        this.attributesBuilder.clearArchives();
+        return this;
+    }
+
+    public ShellActionBuilder withCaptureOutput(final Boolean captureOutput) {
+        this.attributesBuilder.withCaptureOutput(captureOutput);
+        return this;
+    }
+
+    @Override
+    public ShellAction build() {
+        final Node.ConstructionData constructionData = getConstructionData();
+
+        final ShellAction instance = new ShellAction(
+                constructionData,
+                attributesBuilder.build(),
+                executable.get(),
+                ImmutableList.copyOf(environmentVariables));
+
+        addAsChildToAllParents(instance);
+
+        return instance;
+    }
+
+    @Override
+    protected ShellActionBuilder getRuntimeSelfReference() {
+        return this;
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SparkAction.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SparkAction.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SparkAction.java
new file mode 100644
index 0000000..9db041d
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SparkAction.java
@@ -0,0 +1,134 @@
+/**
+ * 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.oozie.fluentjob.api.action;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class representing the Oozie Spark action.
+ * Instances of this class should be built using the builder {@link 
SparkActionBuilder}.
+ *
+ * The properties of the builder can only be set once, an attempt to set them 
a second time will trigger
+ * an {@link IllegalStateException}.
+ *
+ * Builder instances can be used to build several elements, although 
properties already set cannot be changed after
+ * a call to {@link SparkActionBuilder#build} either.
+ */
[email protected]
[email protected]
+public class SparkAction extends Node implements HasAttributes {
+    private final ActionAttributes attributes;
+    private final String master;
+    private final String mode;
+    private final String actionName;
+    private final String actionClass;
+    private final String jar;
+    private final String sparkOpts;
+
+    public SparkAction(final ConstructionData constructionData,
+                       final ActionAttributes attributes,
+                       final String master,
+                       final String mode,
+                       final String actionName,
+                       final String actionClass,
+                       final String jar,
+                       final String sparkOpts) {
+        super(constructionData);
+
+        this.attributes = attributes;
+        this.master = master;
+        this.mode = mode;
+        this.actionName = actionName;
+        this.actionClass = actionClass;
+        this.jar = jar;
+        this.sparkOpts = sparkOpts;
+    }
+
+    public String getResourceManager() {
+        return attributes.getResourceManager();
+    }
+
+    public String getNameNode() {
+        return attributes.getNameNode();
+    }
+
+    public Prepare getPrepare() {
+        return attributes.getPrepare();
+    }
+
+    public Launcher getLauncher() {
+        return attributes.getLauncher();
+    }
+
+    public List<String> getJobXmls() {
+        return attributes.getJobXmls();
+    }
+
+    public String getConfigProperty(final String property) {
+        return attributes.getConfiguration().get(property);
+    }
+
+    public Map<String, String> getConfiguration() {
+        return attributes.getConfiguration();
+    }
+
+    public String getMaster() {
+        return master;
+    }
+
+    public String getMode() {
+        return mode;
+    }
+
+    public String getActionName() {
+        return actionName;
+    }
+
+    public String getActionClass() {
+        return actionClass;
+    }
+
+    public String getJar() {
+        return jar;
+    }
+
+    public String getSparkOpts() {
+        return sparkOpts;
+    }
+
+    public List<String> getArgs() {
+        return attributes.getArgs();
+    }
+
+    public List<String> getFiles() {
+        return attributes.getFiles();
+    }
+
+    public List<String> getArchives() {
+        return attributes.getArchives();
+    }
+
+    public ActionAttributes getAttributes() {
+        return attributes;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SparkActionBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SparkActionBuilder.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SparkActionBuilder.java
new file mode 100644
index 0000000..abc1122
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SparkActionBuilder.java
@@ -0,0 +1,261 @@
+/**
+ * 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.oozie.fluentjob.api.action;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.oozie.fluentjob.api.ModifyOnce;
+
+/**
+ * A builder class for {@link SparkAction}.
+ *
+ * The properties of the builder can only be set once, an attempt to set them 
a second time will trigger
+ * an {@link IllegalStateException}. The properties that are lists are an 
exception to this rule, of course multiple
+ * elements can be added / removed.
+ *
+ * Builder instances can be used to build several elements, although 
properties already set cannot be changed after
+ * a call to {@link SparkActionBuilder#build} either.
+ */
[email protected]
[email protected]
+public class SparkActionBuilder extends 
NodeBuilderBaseImpl<SparkActionBuilder> implements Builder<SparkAction> {
+    private final ActionAttributesBuilder attributesBuilder;
+    private final ModifyOnce<String> master;
+    private final ModifyOnce<String> mode;
+    private final ModifyOnce<String> actionName;
+    private final ModifyOnce<String> actionClass;
+    private final ModifyOnce<String> jar;
+    private final ModifyOnce<String> sparkOpts;
+
+    public static SparkActionBuilder create() {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.create();
+        final ModifyOnce<String> master = new ModifyOnce<>();
+        final ModifyOnce<String> mode = new ModifyOnce<>();
+        final ModifyOnce<String> actionName = new ModifyOnce<>();
+        final ModifyOnce<String> actionClass = new ModifyOnce<>();
+        final ModifyOnce<String> jar = new ModifyOnce<>();
+        final ModifyOnce<String> sparkOpts = new ModifyOnce<>();
+
+        return new SparkActionBuilder(
+                null,
+                builder,
+                master,
+                mode,
+                actionName,
+                actionClass,
+                jar,
+                sparkOpts);
+    }
+
+    public static SparkActionBuilder createFromExistingAction(final 
SparkAction action) {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.createFromExisting(action.getAttributes());
+        final ModifyOnce<String> master = new ModifyOnce<>(action.getMaster());
+        final ModifyOnce<String> mode = new ModifyOnce<>(action.getMode());
+        final ModifyOnce<String> actionName = new 
ModifyOnce<>(action.getActionName());
+        final ModifyOnce<String> actionClass = new 
ModifyOnce<>(action.getActionClass());
+        final ModifyOnce<String> jar = new ModifyOnce<>(action.getJar());
+        final ModifyOnce<String> sparkOpts = new 
ModifyOnce<>(action.getSparkOpts());
+
+        return new SparkActionBuilder(action,
+                builder,
+                master,
+                mode,
+                actionName,
+                actionClass,
+                jar,
+                sparkOpts);
+    }
+
+    public static SparkActionBuilder createFromExistingAction(final Node 
action) {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.createFromAction(action);
+        final ModifyOnce<String> master = new ModifyOnce<>();
+        final ModifyOnce<String> mode = new ModifyOnce<>();
+        final ModifyOnce<String> actionName = new ModifyOnce<>();
+        final ModifyOnce<String> actionClass = new ModifyOnce<>();
+        final ModifyOnce<String> jar = new ModifyOnce<>();
+        final ModifyOnce<String> sparkOpts = new ModifyOnce<>();
+
+        return new SparkActionBuilder(action,
+                builder,
+                master,
+                mode,
+                actionName,
+                actionClass,
+                jar,
+                sparkOpts);
+    }
+
+    SparkActionBuilder(final Node action,
+                       final ActionAttributesBuilder attributesBuilder,
+                       final ModifyOnce<String> master,
+                       final ModifyOnce<String> mode,
+                       final ModifyOnce<String> actionName,
+                       final ModifyOnce<String> actionClass,
+                       final ModifyOnce<String> jar,
+                       final ModifyOnce<String> sparkOpts) {
+        super(action);
+
+        this.attributesBuilder = attributesBuilder;
+        this.master = master;
+        this.mode = mode;
+        this.actionName = actionName;
+        this.actionClass = actionClass;
+        this.jar = jar;
+        this.sparkOpts = sparkOpts;
+    }
+
+    public SparkActionBuilder withResourceManager(final String 
resourceManager) {
+        this.attributesBuilder.withResourceManager(resourceManager);
+        return this;
+    }
+
+    public SparkActionBuilder withNameNode(final String nameNode) {
+        this.attributesBuilder.withNameNode(nameNode);
+        return this;
+    }
+
+    public SparkActionBuilder withPrepare(final Prepare prepare) {
+        this.attributesBuilder.withPrepare(prepare);
+        return this;
+    }
+
+    public SparkActionBuilder withLauncher(final Launcher launcher) {
+        this.attributesBuilder.withLauncher(launcher);
+        return this;
+    }
+
+    public SparkActionBuilder withJobXml(final String jobXml) {
+        this.attributesBuilder.withJobXml(jobXml);
+        return this;
+    }
+
+    public SparkActionBuilder withoutJobXml(final String jobXml) {
+        this.attributesBuilder.withoutJobXml(jobXml);
+        return this;
+    }
+
+    public SparkActionBuilder clearJobXmls() {
+        this.attributesBuilder.clearJobXmls();
+        return this;
+    }
+
+    public SparkActionBuilder withConfigProperty(final String key, final 
String value) {
+        this.attributesBuilder.withConfigProperty(key, value);
+        return this;
+    }
+
+    public SparkActionBuilder withMaster(final String master) {
+        this.master.set(master);
+        return this;
+    }
+
+    public SparkActionBuilder withMode(final String mode) {
+        this.mode.set(mode);
+        return this;
+    }
+
+    public SparkActionBuilder withActionName(final String actionName) {
+        this.actionName.set(actionName);
+        return this;
+    }
+
+    public SparkActionBuilder withActionClass(final String actionClass) {
+        this.actionClass.set(actionClass);
+        return this;
+    }
+
+    public SparkActionBuilder withJar(final String jar) {
+        this.jar.set(jar);
+        return this;
+    }
+
+    public SparkActionBuilder withSparkOpts(final String sparkOpts) {
+        this.sparkOpts.set(sparkOpts);
+        return this;
+    }
+
+    public SparkActionBuilder withArg(final String arg) {
+        this.attributesBuilder.withArg(arg);
+        return this;
+    }
+
+    public SparkActionBuilder withoutArg(final String arg) {
+        this.attributesBuilder.withoutArg(arg);
+        return this;
+    }
+
+    public SparkActionBuilder clearArgs() {
+        this.attributesBuilder.clearArgs();
+        return this;
+    }
+
+    public SparkActionBuilder withFile(final String file) {
+        this.attributesBuilder.withFile(file);
+        return this;
+    }
+
+    public SparkActionBuilder withoutFile(final String file) {
+        this.attributesBuilder.withoutFile(file);
+        return this;
+    }
+
+    public SparkActionBuilder clearFiles() {
+        this.attributesBuilder.clearFiles();
+        return this;
+    }
+
+    public SparkActionBuilder withArchive(final String archive) {
+        this.attributesBuilder.withArchive(archive);
+        return this;
+    }
+
+    public SparkActionBuilder withoutArchive(final String archive) {
+        this.attributesBuilder.withoutArchive(archive);
+        return this;
+    }
+
+    public SparkActionBuilder clearArchive() {
+        this.attributesBuilder.clearArchives();
+        return this;
+    }
+
+    @Override
+    public SparkAction build() {
+        final Node.ConstructionData constructionData = getConstructionData();
+
+        final SparkAction instance = new SparkAction(
+                constructionData,
+                attributesBuilder.build(),
+                master.get(),
+                mode.get(),
+                actionName.get(),
+                actionClass.get(),
+                jar.get(),
+                sparkOpts.get());
+
+        addAsChildToAllParents(instance);
+
+        return instance;
+    }
+
+    @Override
+    protected SparkActionBuilder getRuntimeSelfReference() {
+        return this;
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SqoopAction.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SqoopAction.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SqoopAction.java
new file mode 100644
index 0000000..3fbf08a
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SqoopAction.java
@@ -0,0 +1,99 @@
+/**
+ * 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.oozie.fluentjob.api.action;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class representing the Oozie Sqoop action.
+ * Instances of this class should be built using the builder {@link 
SqoopActionBuilder}.
+ *
+ * The properties of the builder can only be set once, an attempt to set them 
a second time will trigger
+ * an {@link IllegalStateException}.
+ *
+ * Builder instances can be used to build several elements, although 
properties already set cannot be changed after
+ * a call to {@link SqoopActionBuilder#build} either.
+ */
[email protected]
[email protected]
+public class SqoopAction extends Node implements HasAttributes {
+    private final ActionAttributes attributes;
+    private final String command;
+
+    SqoopAction(final ConstructionData constructionData,
+                final ActionAttributes attributes,
+                final String command) {
+        super(constructionData);
+
+        this.attributes = attributes;
+        this.command = command;
+    }
+
+    public String getResourceManager() {
+        return attributes.getResourceManager();
+    }
+
+    public String getNameNode() {
+        return attributes.getNameNode();
+    }
+
+    public Prepare getPrepare() {
+        return attributes.getPrepare();
+    }
+
+    public Launcher getLauncher() {
+        return attributes.getLauncher();
+    }
+
+    public List<String> getJobXmls() {
+        return attributes.getJobXmls();
+    }
+
+    public String getConfigProperty(final String property) {
+        return attributes.getConfiguration().get(property);
+    }
+
+    public Map<String, String> getConfiguration() {
+        return attributes.getConfiguration();
+    }
+
+    public String getCommand() {
+        return command;
+    }
+
+    public List<String> getArguments() {
+        return attributes.getArgs();
+    }
+
+    public List<String> getFiles() {
+        return attributes.getFiles();
+    }
+
+    public List<String> getArchives() {
+        return attributes.getArchives();
+    }
+
+    public ActionAttributes getAttributes() {
+        return attributes;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SqoopActionBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SqoopActionBuilder.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SqoopActionBuilder.java
new file mode 100644
index 0000000..8b7e7e5
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SqoopActionBuilder.java
@@ -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.oozie.fluentjob.api.action;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.oozie.fluentjob.api.ModifyOnce;
+
+/**
+ * A builder class for {@link SqoopAction}.
+ *
+ * The properties of the builder can only be set once, an attempt to set them 
a second time will trigger
+ * an {@link IllegalStateException}. The properties that are lists are an 
exception to this rule, of course multiple
+ * elements can be added / removed.
+ *
+ * Builder instances can be used to build several elements, although 
properties already set cannot be changed after
+ * a call to {@link SqoopActionBuilder#build} either.
+ */
[email protected]
[email protected]
+public class SqoopActionBuilder extends 
NodeBuilderBaseImpl<SqoopActionBuilder> implements Builder<SqoopAction> {
+    private final ActionAttributesBuilder attributesBuilder;
+    private final ModifyOnce<String> command;
+
+    public static SqoopActionBuilder create() {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.create();
+        final ModifyOnce<String> command = new ModifyOnce<>();
+
+        return new SqoopActionBuilder(
+                null,
+                builder,
+                command);
+    }
+
+    public static SqoopActionBuilder createFromExistingAction(final 
SqoopAction action) {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.createFromExisting(action.getAttributes());
+        final ModifyOnce<String> command = new 
ModifyOnce<>(action.getCommand());
+
+        return new SqoopActionBuilder(action,
+                builder,
+                command);
+    }
+
+    public static SqoopActionBuilder createFromExistingAction(final Node 
action) {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.createFromAction(action);
+        final ModifyOnce<String> command = new ModifyOnce<>();
+
+        return new SqoopActionBuilder(action,
+                builder,
+                command);
+    }
+
+    private SqoopActionBuilder(final Node action,
+                               final ActionAttributesBuilder attributesBuilder,
+                               final ModifyOnce<String> command) {
+        super(action);
+
+        this.attributesBuilder = attributesBuilder;
+        this.command = command;
+    }
+
+    public SqoopActionBuilder withResourceManager(final String 
resourceManager) {
+        this.attributesBuilder.withResourceManager(resourceManager);
+        return this;
+    }
+
+    public SqoopActionBuilder withNameNode(final String nameNode) {
+        this.attributesBuilder.withNameNode(nameNode);
+        return this;
+    }
+
+    public SqoopActionBuilder withPrepare(final Prepare prepare) {
+        this.attributesBuilder.withPrepare(prepare);
+        return this;
+    }
+
+    public SqoopActionBuilder withLauncher(final Launcher launcher) {
+        this.attributesBuilder.withLauncher(launcher);
+        return this;
+    }
+
+    public SqoopActionBuilder withJobXml(final String jobXml) {
+        this.attributesBuilder.withJobXml(jobXml);
+        return this;
+    }
+
+    public SqoopActionBuilder withoutJobXml(final String jobXml) {
+        this.attributesBuilder.withoutJobXml(jobXml);
+        return this;
+    }
+
+    public SqoopActionBuilder clearJobXmls() {
+        this.attributesBuilder.clearJobXmls();
+        return this;
+    }
+
+    public SqoopActionBuilder withConfigProperty(final String key, final 
String value) {
+        this.attributesBuilder.withConfigProperty(key, value);
+        return this;
+    }
+
+    public SqoopActionBuilder withCommand(final String command) {
+        this.command.set(command);
+        return this;
+    }
+
+    public SqoopActionBuilder withArgument(final String argument) {
+        this.attributesBuilder.withArg(argument);
+        return this;
+    }
+
+    public SqoopActionBuilder withoutArgument(final String argument) {
+        this.attributesBuilder.withoutArg(argument);
+        return this;
+    }
+
+    public SqoopActionBuilder clearArguments() {
+        this.attributesBuilder.clearArgs();
+        return this;
+    }
+
+    public SqoopActionBuilder withFile(final String file) {
+        this.attributesBuilder.withFile(file);
+        return this;
+    }
+
+    public SqoopActionBuilder withoutFile(final String file) {
+        this.attributesBuilder.withoutFile(file);
+        return this;
+    }
+
+    public SqoopActionBuilder clearFiles() {
+        this.attributesBuilder.clearFiles();
+        return this;
+    }
+
+    public SqoopActionBuilder withArchive(final String archive) {
+        this.attributesBuilder.withArchive(archive);
+        return this;
+    }
+
+    public SqoopActionBuilder withoutArchive(final String archive) {
+        this.attributesBuilder.withoutArchive(archive);
+        return this;
+    }
+
+    public SqoopActionBuilder clearArchives() {
+        this.attributesBuilder.clearArchives();
+        return this;
+    }
+
+    @Override
+    public SqoopAction build() {
+        final Node.ConstructionData constructionData = getConstructionData();
+
+        final SqoopAction instance = new SqoopAction(
+                constructionData,
+                attributesBuilder.build(),
+                command.get());
+
+        addAsChildToAllParents(instance);
+
+        return instance;
+    }
+
+    @Override
+    protected SqoopActionBuilder getRuntimeSelfReference() {
+        return this;
+    }
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SshAction.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SshAction.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SshAction.java
new file mode 100644
index 0000000..8e73ce6
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SshAction.java
@@ -0,0 +1,74 @@
+/**
+ * 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.oozie.fluentjob.api.action;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+import java.util.List;
+
+/**
+ * A class representing the Oozie ssh action.
+ * Instances of this class should be built using the builder {@link 
SshActionBuilder}.
+ *
+ * The properties of the builder can only be set once, an attempt to set them 
a second time will trigger
+ * an {@link IllegalStateException}.
+ *
+ * Builder instances can be used to build several elements, although 
properties already set cannot be changed after
+ * a call to {@link SshActionBuilder#build} either.
+ */
[email protected]
[email protected]
+public class SshAction extends Node implements HasAttributes {
+    private final ActionAttributes attributes;
+    private final String host;
+    private final String command;
+
+
+    SshAction(final ConstructionData constructionData,
+              final ActionAttributes attributes,
+              final String host,
+              final String command) {
+        super(constructionData);
+
+        this.attributes = attributes;
+        this.host = host;
+        this.command = command;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public String getCommand() {
+        return command;
+    }
+
+    public List<String> getArgs() {
+        return attributes.getArgs();
+    }
+
+    public boolean isCaptureOutput() {
+        return attributes.isCaptureOutput();
+    }
+
+    public ActionAttributes getAttributes() {
+        return attributes;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/oozie/blob/8a0a6487/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SshActionBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SshActionBuilder.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SshActionBuilder.java
new file mode 100644
index 0000000..1da74eb
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/SshActionBuilder.java
@@ -0,0 +1,136 @@
+/**
+ * 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.oozie.fluentjob.api.action;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.oozie.fluentjob.api.ModifyOnce;
+
+/**
+ * A builder class for {@link EmailAction}.
+ *
+ * The properties of the builder can only be set once, an attempt to set them 
a second time will trigger
+ * an {@link IllegalStateException}. The properties that are lists are an 
exception to this rule, of course multiple
+ * elements can be added / removed.
+ *
+ * Builder instances can be used to build several elements, although 
properties already set cannot be changed after
+ * a call to {@link SshActionBuilder#build} either.
+ */
[email protected]
[email protected]
+public class SshActionBuilder extends NodeBuilderBaseImpl<SshActionBuilder> 
implements Builder<SshAction> {
+    private final ActionAttributesBuilder attributesBuilder;
+    private final ModifyOnce<String> host;
+    private final ModifyOnce<String> command;
+
+    public static SshActionBuilder create() {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.create();
+        final ModifyOnce<String> host = new ModifyOnce<>();
+        final ModifyOnce<String> command = new ModifyOnce<>();
+
+        return new SshActionBuilder(
+                null,
+                builder,
+                host,
+                command);
+    }
+
+    public static SshActionBuilder createFromExistingAction(final SshAction 
action) {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.createFromExisting(action.getAttributes());
+        final ModifyOnce<String> host = new ModifyOnce<>(action.getHost());
+        final ModifyOnce<String> command = new 
ModifyOnce<>(action.getCommand());
+
+        return new SshActionBuilder(action,
+                builder,
+                host,
+                command);
+    }
+
+    public static SshActionBuilder createFromExistingAction(final Node action) 
{
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.createFromAction(action);
+        final ModifyOnce<String> host = new ModifyOnce<>();
+        final ModifyOnce<String> command = new ModifyOnce<>();
+
+        return new SshActionBuilder(action,
+                builder,
+                host,
+                command);
+    }
+
+    private SshActionBuilder(final Node action,
+                             final ActionAttributesBuilder attributesBuilder,
+                             final ModifyOnce<String> host,
+                             final ModifyOnce<String> command) {
+        super(action);
+
+        this.attributesBuilder = attributesBuilder;
+        this.host = host;
+        this.command = command;
+    }
+
+    public SshActionBuilder withHost(final String host) {
+        this.host.set(host);
+        return this;
+    }
+
+    public SshActionBuilder withCommand(final String command) {
+        this.command.set(command);
+        return this;
+    }
+
+    public SshActionBuilder withArg(final String arg) {
+        this.attributesBuilder.withArg(arg);
+        return this;
+    }
+
+    public SshActionBuilder withoutArg(final String arg) {
+        this.attributesBuilder.withoutArg(arg);
+        return this;
+    }
+
+    public SshActionBuilder clearArgs() {
+        this.attributesBuilder.clearArgs();
+        return this;
+    }
+
+    public SshActionBuilder withCaptureOutput(final Boolean captureOutput) {
+        this.attributesBuilder.withCaptureOutput(captureOutput);
+        return this;
+    }
+
+    @Override
+    public SshAction build() {
+        final Node.ConstructionData constructionData = getConstructionData();
+
+        final SshAction instance = new SshAction(
+                constructionData,
+                attributesBuilder.build(),
+                host.get(),
+                command.get());
+
+        addAsChildToAllParents(instance);
+
+        return instance;
+    }
+
+    @Override
+    protected SshActionBuilder getRuntimeSelfReference() {
+        return this;
+    }
+}

Reply via email to