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/HasAttributes.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/HasAttributes.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/HasAttributes.java
new file mode 100644
index 0000000..9aec459
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/HasAttributes.java
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oozie.fluentjob.api.action;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+/**
+ * Designates whether the implementing POJO has {@link ActionAttributes}.
+ */
[email protected]
[email protected]
+public interface HasAttributes {
+    ActionAttributes getAttributes();
+}

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/Hive2Action.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Hive2Action.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Hive2Action.java
new file mode 100644
index 0000000..fbaf55d
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Hive2Action.java
@@ -0,0 +1,65 @@
+/**
+ * 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 class representing the Oozie Hive2 action.
+ * Instances of this class should be built using the builder {@link 
Hive2ActionBuilder}.
+ *
+ * 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 Hive2ActionBuilder#build} either.
+ */
[email protected]
[email protected]
+public class Hive2Action extends HiveAction {
+    private final String jdbcUrl;
+    private final String password;
+
+    public Hive2Action(final ConstructionData constructionData,
+                       final ActionAttributes attributes,
+                       final String jdbcUrl,
+                       final String password,
+                       final String script,
+                       final String query,
+                       final ImmutableList<String> params) {
+        super(constructionData,
+              attributes,
+              script,
+              query,
+              params);
+
+        this.jdbcUrl = jdbcUrl;
+        this.password = password;
+    }
+
+    public String getJdbcUrl() {
+        return jdbcUrl;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+}
\ 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/Hive2ActionBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Hive2ActionBuilder.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Hive2ActionBuilder.java
new file mode 100644
index 0000000..63b8f47
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Hive2ActionBuilder.java
@@ -0,0 +1,259 @@
+/**
+ * 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 Hive2Action}.
+ *
+ * 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 Hive2ActionBuilder#build} either.
+ */
[email protected]
[email protected]
+public class Hive2ActionBuilder extends 
NodeBuilderBaseImpl<Hive2ActionBuilder> implements Builder<Hive2Action> {
+    private final HiveActionBuilder delegate;
+    private final ModifyOnce<String> jdbcUrl;
+    private final ModifyOnce<String> password;
+
+    public static Hive2ActionBuilder create() {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.create();
+        final ModifyOnce<String> jdbcUrl = new ModifyOnce<>();
+        final ModifyOnce<String> password = new ModifyOnce<>();
+        final ModifyOnce<String> script = new ModifyOnce<>();
+        final ModifyOnce<String> query = new ModifyOnce<>();
+        final List<String> params = new ArrayList<>();
+
+        return new Hive2ActionBuilder(
+                null,
+                builder,
+                jdbcUrl,
+                password,
+                script,
+                query,
+                params);
+    }
+
+    public static Hive2ActionBuilder createFromExistingAction(final 
Hive2Action action) {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.createFromExisting(action.getAttributes());
+        final ModifyOnce<String> jdbcUrl = new 
ModifyOnce<>(action.getJdbcUrl());
+        final ModifyOnce<String> password = new 
ModifyOnce<>(action.getPassword());
+        final ModifyOnce<String> script = new ModifyOnce<>(action.getScript());
+        final ModifyOnce<String> query = new ModifyOnce<>(action.getQuery());
+        final List<String> params = new ArrayList<>(action.getParams());
+
+        return new Hive2ActionBuilder(action,
+                builder,
+                jdbcUrl,
+                password,
+                script,
+                query,
+                params);
+    }
+
+    public static Hive2ActionBuilder createFromExistingAction(final Node 
action) {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.createFromAction(action);
+        final ModifyOnce<String> jdbcUrl = new ModifyOnce<>();
+        final ModifyOnce<String> password = new ModifyOnce<>();
+        final ModifyOnce<String> script = new ModifyOnce<>();
+        final ModifyOnce<String> query = new ModifyOnce<>();
+        final List<String> params = new ArrayList<>();
+
+        return new Hive2ActionBuilder(action,
+                builder,
+                jdbcUrl,
+                password,
+                script,
+                query,
+                params);
+    }
+
+    Hive2ActionBuilder(final Node action,
+                       final ActionAttributesBuilder attributesBuilder,
+                       final ModifyOnce<String> jdbcUrl,
+                       final ModifyOnce<String> password,
+                       final ModifyOnce<String> script,
+                       final ModifyOnce<String> query,
+                       final List<String> params) {
+        super(action);
+
+        this.delegate = new HiveActionBuilder(action,
+                attributesBuilder,
+                script,
+                query,
+                params);
+
+        this.jdbcUrl = jdbcUrl;
+        this.password = password;
+    }
+
+    public Hive2ActionBuilder withResourceManager(final String 
resourceManager) {
+        delegate.withResourceManager(resourceManager);
+        return this;
+    }
+
+    public Hive2ActionBuilder withNameNode(final String nameNode) {
+        delegate.withNameNode(nameNode);
+        return this;
+    }
+
+    public Hive2ActionBuilder withPrepare(final Prepare prepare) {
+        delegate.withPrepare(prepare);
+        return this;
+    }
+
+    public Hive2ActionBuilder withLauncher(final Launcher launcher) {
+        delegate.withLauncher(launcher);
+        return this;
+    }
+
+    public Hive2ActionBuilder withJobXml(final String jobXml) {
+        delegate.withJobXml(jobXml);
+        return this;
+    }
+
+    public Hive2ActionBuilder withoutJobXml(final String jobXml) {
+        delegate.withoutJobXml(jobXml);
+        return this;
+    }
+
+    public Hive2ActionBuilder clearJobXmls() {
+        delegate.clearJobXmls();
+        return this;
+    }
+
+    public Hive2ActionBuilder withConfigProperty(final String key, final 
String value) {
+        delegate.withConfigProperty(key, value);
+        return this;
+    }
+
+    public Hive2ActionBuilder withScript(final String script) {
+        delegate.withScript(script);
+        return this;
+    }
+
+    public Hive2ActionBuilder withQuery(final String query) {
+        delegate.withQuery(query);
+        return this;
+    }
+
+    public Hive2ActionBuilder withParam(final String param) {
+        delegate.withParam(param);
+        return this;
+    }
+
+    public Hive2ActionBuilder withoutParam(final String param) {
+        delegate.withoutParam(param);
+        return this;
+    }
+
+    public Hive2ActionBuilder clearParams() {
+        delegate.clearParams();
+        return this;
+    }
+
+    public Hive2ActionBuilder withArg(final String arg) {
+        delegate.withArg(arg);
+        return this;
+    }
+
+    public Hive2ActionBuilder withoutArg(final String arg) {
+        delegate.withoutArg(arg);
+        return this;
+    }
+
+    public Hive2ActionBuilder clearArgs() {
+        delegate.clearArgs();
+        return this;
+    }
+
+    public Hive2ActionBuilder withFile(final String file) {
+        delegate.withFile(file);
+        return this;
+    }
+
+    public Hive2ActionBuilder withoutFile(final String file) {
+        delegate.withoutFile(file);
+        return this;
+    }
+
+    public Hive2ActionBuilder clearFiles() {
+        delegate.clearFiles();
+        return this;
+    }
+
+    public Hive2ActionBuilder withArchive(final String archive) {
+        delegate.withArchive(archive);
+        return this;
+    }
+
+    public Hive2ActionBuilder withoutArchive(final String archive) {
+        delegate.withoutArchive(archive);
+        return this;
+    }
+
+    public Hive2ActionBuilder clearArchives() {
+        delegate.clearArchives();
+        return this;
+    }
+
+    public Hive2ActionBuilder withJdbcUrl(final String jdbcUrl) {
+        this.jdbcUrl.set(jdbcUrl);
+        return this;
+    }
+
+    public Hive2ActionBuilder withPassword(final String password) {
+        this.password.set(password);
+        return this;
+    }
+
+    @Override
+    public Hive2Action build() {
+        final Node.ConstructionData constructionData = getConstructionData();
+
+        final Hive2Action instance = new Hive2Action(
+                constructionData,
+                delegate.getAttributesBuilder().build(),
+                jdbcUrl.get(),
+                password.get(),
+                delegate.getScript().get(),
+                delegate.query.get(),
+                ImmutableList.copyOf(delegate.getParams()));
+
+        addAsChildToAllParents(instance);
+
+        return instance;
+    }
+
+    @Override
+    protected Hive2ActionBuilder 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/HiveAction.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/HiveAction.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/HiveAction.java
new file mode 100644
index 0000000..7b12624
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/HiveAction.java
@@ -0,0 +1,56 @@
+/**
+ * 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 class representing the Oozie Hive action.
+ * Instances of this class should be built using the builder {@link 
HiveActionBuilder}.
+ *
+ * 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 HiveActionBuilder#build} either.
+ */
[email protected]
[email protected]
+public class HiveAction extends PigAction {
+    private final String query;
+
+    HiveAction(final ConstructionData constructionData,
+               final ActionAttributes attributes,
+               final String script,
+               final String query,
+               final ImmutableList<String> params) {
+        super(constructionData,
+              attributes,
+              script,
+              params);
+
+        this.query = query;
+    }
+
+    public String getQuery() {
+        return query;
+    }
+}
\ 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/HiveActionBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/HiveActionBuilder.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/HiveActionBuilder.java
new file mode 100644
index 0000000..86bb398
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/HiveActionBuilder.java
@@ -0,0 +1,242 @@
+/**
+ * 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 HiveAction}.
+ *
+ * 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 HiveActionBuilder#build} either.
+ */
[email protected]
[email protected]
+public class HiveActionBuilder extends NodeBuilderBaseImpl<HiveActionBuilder> 
implements Builder<HiveAction> {
+    private final PigActionBuilder delegate;
+    protected final ModifyOnce<String> query;
+
+    public static HiveActionBuilder create() {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.create();
+        final ModifyOnce<String> script = new ModifyOnce<>();
+        final ModifyOnce<String> query = new ModifyOnce<>();
+        final List<String> params = new ArrayList<>();
+
+        return new HiveActionBuilder(
+                null,
+                builder,
+                script,
+                query,
+                params);
+    }
+
+    public static HiveActionBuilder createFromExistingAction(final HiveAction 
action) {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.createFromExisting(action.getAttributes());
+        final ModifyOnce<String> script = new ModifyOnce<>(action.getScript());
+        final ModifyOnce<String> query = new ModifyOnce<>(action.getQuery());
+        final List<String> params = new ArrayList<>(action.getParams());
+
+        return new HiveActionBuilder(action,
+                builder,
+                script,
+                query,
+                params);
+    }
+
+    public static HiveActionBuilder createFromExistingAction(final Node 
action) {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.createFromAction(action);
+        final ModifyOnce<String> script = new ModifyOnce<>();
+        final ModifyOnce<String> query = new ModifyOnce<>();
+        final List<String> params = new ArrayList<>();
+
+        return new HiveActionBuilder(action,
+                builder,
+                script,
+                query,
+                params);
+    }
+
+    HiveActionBuilder(final Node action,
+                      final ActionAttributesBuilder attributesBuilder,
+                      final ModifyOnce<String> script,
+                      final ModifyOnce<String> query,
+                      final List<String> params) {
+        super(action);
+
+        this.delegate = new PigActionBuilder(action,
+                                             attributesBuilder,
+                                             script,
+                                             params);
+
+        this.query = query;
+    }
+
+    public HiveActionBuilder withResourceManager(final String resourceManager) 
{
+        this.delegate.withResourceManager(resourceManager);
+        return this;
+    }
+
+    public HiveActionBuilder withNameNode(final String nameNode) {
+        this.delegate.withNameNode(nameNode);
+        return this;
+    }
+
+    public HiveActionBuilder withPrepare(final Prepare prepare) {
+        this.delegate.withPrepare(prepare);
+        return this;
+    }
+
+    public HiveActionBuilder withLauncher(final Launcher launcher) {
+        this.delegate.withLauncher(launcher);
+        return this;
+    }
+
+    public HiveActionBuilder withJobXml(final String jobXml) {
+        this.delegate.withJobXml(jobXml);
+        return this;
+    }
+
+    public HiveActionBuilder withoutJobXml(final String jobXml) {
+        this.delegate.withoutJobXml(jobXml);
+        return this;
+    }
+
+    public HiveActionBuilder clearJobXmls() {
+        this.delegate.clearJobXmls();
+        return this;
+    }
+
+    public HiveActionBuilder withConfigProperty(final String key, final String 
value) {
+        this.delegate.withConfigProperty(key, value);
+        return this;
+    }
+
+    public HiveActionBuilder withScript(final String script) {
+        this.delegate.withScript(script);
+        return this;
+    }
+
+    public HiveActionBuilder withQuery(final String query) {
+        this.query.set(query);
+        return this;
+    }
+
+    public HiveActionBuilder withParam(final String param) {
+        this.delegate.withParam(param);
+        return this;
+    }
+
+    public HiveActionBuilder withoutParam(final String param) {
+        this.delegate.withoutParam(param);
+        return this;
+    }
+
+    public HiveActionBuilder clearParams() {
+        this.delegate.clearParams();
+        return this;
+    }
+
+    public HiveActionBuilder withArg(final String arg) {
+        this.delegate.withArg(arg);
+        return this;
+    }
+
+    public HiveActionBuilder withoutArg(final String arg) {
+        this.delegate.withoutArg(arg);
+        return this;
+    }
+
+    public HiveActionBuilder clearArgs() {
+        this.delegate.clearArgs();
+        return this;
+    }
+
+    public HiveActionBuilder withFile(final String file) {
+        this.delegate.withFile(file);
+        return this;
+    }
+
+    public HiveActionBuilder withoutFile(final String file) {
+        this.delegate.withoutFile(file);
+        return this;
+    }
+
+    public HiveActionBuilder clearFiles() {
+        this.delegate.clearFiles();
+        return this;
+    }
+
+    public HiveActionBuilder withArchive(final String archive) {
+        this.delegate.withArchive(archive);
+        return this;
+    }
+
+    public HiveActionBuilder withoutArchive(final String archive) {
+        this.delegate.withoutArchive(archive);
+        return this;
+    }
+
+    public HiveActionBuilder clearArchives() {
+        this.delegate.clearArchives();
+        return this;
+    }
+
+    ActionAttributesBuilder getAttributesBuilder() {
+        return delegate.attributesBuilder;
+    }
+
+    ModifyOnce<String> getScript() {
+        return delegate.script;
+    }
+
+    List<String> getParams() {
+        return delegate.params;
+    }
+
+    @Override
+    public HiveAction build() {
+        final Node.ConstructionData constructionData = getConstructionData();
+
+        final HiveAction instance = new HiveAction(
+                constructionData,
+                getAttributesBuilder().build(),
+                getScript().get(),
+                query.get(),
+                ImmutableList.copyOf(getParams()));
+
+        addAsChildToAllParents(instance);
+
+        return instance;
+    }
+
+    @Override
+    protected HiveActionBuilder 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/JavaAction.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/JavaAction.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/JavaAction.java
new file mode 100644
index 0000000..cadc825
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/JavaAction.java
@@ -0,0 +1,119 @@
+/**
+ * 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 Java action.
+ * Instances of this class should be built using the builder {@link 
JavaActionBuilder}.
+ *
+ * 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 JavaActionBuilder#build} either.
+ */
[email protected]
[email protected]
+public class JavaAction extends Node {
+    private final ActionAttributes attributes;
+    private final String mainClass;
+    private final String javaOptsString;
+    private final ImmutableList<String> javaOpts;
+
+
+    JavaAction(final ConstructionData constructionData,
+               final ActionAttributes attributes,
+               final String mainClass,
+               final String javaOptsString,
+               final ImmutableList<String> javaOpts) {
+        super(constructionData);
+
+        this.attributes = attributes;
+        this.mainClass = mainClass;
+        this.javaOptsString = javaOptsString;
+        this.javaOpts = javaOpts;
+    }
+
+    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 getMainClass() {
+        return mainClass;
+    }
+
+    public String getJavaOptsString() {
+        return javaOptsString;
+    }
+
+    public List<String> getJavaOpts() {
+        return javaOpts;
+    }
+
+    public List<String> getArgs() {
+        return attributes.getArgs();
+    }
+
+    public List<String> getFiles() {
+        return attributes.getFiles();
+    }
+
+    public List<String> getArchives() {
+        return attributes.getArchives();
+    }
+
+    public boolean isCaptureOutput() {
+        return attributes.isCaptureOutput();
+    }
+
+    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/JavaActionBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/JavaActionBuilder.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/JavaActionBuilder.java
new file mode 100644
index 0000000..af991cd
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/JavaActionBuilder.java
@@ -0,0 +1,235 @@
+/**
+ * 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 JavaAction}.
+ *
+ * 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 JavaActionBuilder#build} either.
+ */
[email protected]
[email protected]
+public class JavaActionBuilder extends NodeBuilderBaseImpl<JavaActionBuilder> 
implements Builder<JavaAction> {
+    private final ActionAttributesBuilder attributesBuilder;
+    private final ModifyOnce<String> mainClass;
+    private final ModifyOnce<String> javaOptsString;
+    private final List<String> javaOpts;
+
+    public static JavaActionBuilder create() {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.create();
+        final ModifyOnce<String> mainClass = new ModifyOnce<>();
+        final ModifyOnce<String> javaOptsString = new ModifyOnce<>();
+        final List<String> javaOpts = new ArrayList<>();
+
+        return new JavaActionBuilder(
+                null,
+                builder,
+                mainClass,
+                javaOptsString,
+                javaOpts);
+    }
+
+    public static JavaActionBuilder createFromExistingAction(final JavaAction 
action) {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.createFromExisting(action.getAttributes());
+        final ModifyOnce<String> mainClass = new 
ModifyOnce<>(action.getMainClass());
+        final ModifyOnce<String> javaOptsString = new 
ModifyOnce<>(action.getJavaOptsString());
+        final List<String> javaOpts = new ArrayList<>(action.getJavaOpts());
+
+        return new JavaActionBuilder(action,
+                builder,
+                mainClass,
+                javaOptsString,
+                javaOpts);
+    }
+
+    public static JavaActionBuilder createFromExistingAction(final Node 
action) {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.createFromAction(action);
+        final ModifyOnce<String> mainClass = new ModifyOnce<>();
+        final ModifyOnce<String> javaOptsString = new ModifyOnce<>();
+        final List<String> javaOpts = new ArrayList<>();
+
+        return new JavaActionBuilder(action,
+                builder,
+                mainClass,
+                javaOptsString,
+                javaOpts);
+    }
+
+    private JavaActionBuilder(final Node action,
+                              final ActionAttributesBuilder attributesBuilder,
+                              final ModifyOnce<String> mainClass,
+                              final ModifyOnce<String> javaOptsString,
+                              final List<String> javaOpts) {
+        super(action);
+
+        this.attributesBuilder = attributesBuilder;
+        this.mainClass = mainClass;
+        this.javaOptsString = javaOptsString;
+        this.javaOpts = javaOpts;
+    }
+
+    public JavaActionBuilder withResourceManager(final String resourceManager) 
{
+        this.attributesBuilder.withResourceManager(resourceManager);
+        return this;
+    }
+
+    public JavaActionBuilder withNameNode(final String nameNode) {
+        this.attributesBuilder.withNameNode(nameNode);
+        return this;
+    }
+
+    public JavaActionBuilder withPrepare(final Prepare prepare) {
+        this.attributesBuilder.withPrepare(prepare);
+        return this;
+    }
+
+    public JavaActionBuilder withLauncher(final Launcher launcher) {
+        this.attributesBuilder.withLauncher(launcher);
+        return this;
+    }
+
+    public JavaActionBuilder withJobXml(final String jobXml) {
+        this.attributesBuilder.withJobXml(jobXml);
+        return this;
+    }
+
+    public JavaActionBuilder withoutJobXml(final String jobXml) {
+        this.attributesBuilder.withoutJobXml(jobXml);
+        return this;
+    }
+
+    public JavaActionBuilder clearJobXmls() {
+        this.attributesBuilder.clearJobXmls();
+        return this;
+    }
+
+    public JavaActionBuilder withConfigProperty(final String key, final String 
value) {
+        this.attributesBuilder.withConfigProperty(key, value);
+        return this;
+    }
+
+    public JavaActionBuilder withMainClass(final String mainClass) {
+        this.mainClass.set(mainClass);
+        return this;
+    }
+
+    public JavaActionBuilder withJavaOptsString(final String javaOptsString) {
+        this.javaOptsString.set(javaOptsString);
+        return this;
+    }
+
+    public JavaActionBuilder withJavaOpt(final String javaOpt) {
+        this.javaOpts.add(javaOpt);
+        return this;
+    }
+
+    public JavaActionBuilder withoutJavaOpt(final String javaOpt) {
+        this.javaOpts.remove(javaOpt);
+        return this;
+    }
+
+    public JavaActionBuilder clearJavaOpts() {
+        this.javaOpts.clear();
+        return this;
+    }
+
+    public JavaActionBuilder withArg(final String arg) {
+        this.attributesBuilder.withArg(arg);
+        return this;
+    }
+
+    public JavaActionBuilder withoutArg(final String arg) {
+        this.attributesBuilder.withoutArg(arg);
+        return this;
+    }
+
+    public JavaActionBuilder clearArgs() {
+        this.attributesBuilder.clearArgs();
+        return this;
+    }
+
+    public JavaActionBuilder withFile(final String file) {
+        this.attributesBuilder.withFile(file);
+        return this;
+    }
+
+    public JavaActionBuilder withoutFile(final String file) {
+        this.attributesBuilder.withoutFile(file);
+        return this;
+    }
+
+    public JavaActionBuilder clearFiles() {
+        this.attributesBuilder.clearFiles();
+        return this;
+    }
+
+    public JavaActionBuilder withArchive(final String archive) {
+        this.attributesBuilder.withArchive(archive);
+        return this;
+    }
+
+    public JavaActionBuilder withoutArchive(final String archive) {
+        this.attributesBuilder.withoutArchive(archive);
+        return this;
+    }
+
+    public JavaActionBuilder clearArchives() {
+        this.attributesBuilder.clearArchives();
+        return this;
+    }
+
+    public JavaActionBuilder withCaptureOutput(final Boolean captureOutput) {
+        this.attributesBuilder.withCaptureOutput(captureOutput);
+        return this;
+    }
+
+    @Override
+    public JavaAction build() {
+        final Node.ConstructionData constructionData = getConstructionData();
+
+        final JavaAction instance = new JavaAction(
+                constructionData,
+                attributesBuilder.build(),
+                mainClass.get(),
+                javaOptsString.get(),
+                ImmutableList.copyOf(javaOpts));
+
+        addAsChildToAllParents(instance);
+
+        return instance;
+    }
+
+    @Override
+    protected JavaActionBuilder 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/Launcher.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Launcher.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Launcher.java
new file mode 100644
index 0000000..aa41564
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Launcher.java
@@ -0,0 +1,81 @@
+/**
+ * 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.workflow.Workflow;
+
+/**
+ * Represents the {@code <launcher>} element and its siblings inside workflow 
XML / XSD.
+ * <p>
+ * By assigning non-{@code null} field values, the resulting parent {@code 
<workflow>} will have its
+ * optional {@code <launcher>} element and its siblings filled.
+ * <p>
+ * This class is used only as part of a {@link Workflow}, isn't
+ * to be used alone with Jobs API.
+ */
[email protected]
[email protected]
+public class Launcher {
+    private final long memoryMb;
+    private final long vCores;
+    private final String queue;
+    private final String sharelib;
+    private final String viewAcl;
+    private final String modifyAcl;
+
+    Launcher(final long memoryMb,
+                    final long vCores,
+                    final String queue,
+                    final String sharelib,
+                    final String viewAcl,
+                    final String modifyAcl) {
+        this.memoryMb = memoryMb;
+        this.vCores = vCores;
+        this.queue = queue;
+        this.sharelib = sharelib;
+        this.viewAcl = viewAcl;
+        this.modifyAcl = modifyAcl;
+    }
+
+    public long getMemoryMb() {
+        return memoryMb;
+    }
+
+    public long getVCores() {
+        return vCores;
+    }
+
+    public String getQueue() {
+        return queue;
+    }
+
+    public String getSharelib() {
+        return sharelib;
+    }
+
+    public String getViewAcl() {
+        return viewAcl;
+    }
+
+    public String getModifyAcl() {
+        return modifyAcl;
+    }
+}

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/LauncherBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/LauncherBuilder.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/LauncherBuilder.java
new file mode 100644
index 0000000..3e4ea1d
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/LauncherBuilder.java
@@ -0,0 +1,92 @@
+/**
+ * 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 Launcher}.
+ *
+ * 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 LauncherBuilder#build} either.
+ */
[email protected]
[email protected]
+public class LauncherBuilder implements Builder<Launcher> {
+    private final ModifyOnce<Long> memoryMb;
+    private final ModifyOnce<Long> vCores;
+    private final ModifyOnce<String> queue;
+    private final ModifyOnce<String> sharelib;
+    private final ModifyOnce<String> viewAcl;
+    private final ModifyOnce<String> modifyAcl;
+
+    public LauncherBuilder() {
+        this.memoryMb = new ModifyOnce<>();
+        this.vCores = new ModifyOnce<>();
+        this.queue = new ModifyOnce<>();
+        this.sharelib = new ModifyOnce<>();
+        this.viewAcl = new ModifyOnce<>();
+        this.modifyAcl = new ModifyOnce<>();
+    }
+
+    @Override
+    public Launcher build() {
+        return new Launcher(memoryMb.get(),
+                vCores.get(),
+                queue.get(),
+                sharelib.get(),
+                viewAcl.get(),
+                modifyAcl.get());
+    }
+
+    public LauncherBuilder withMemoryMb(final long memoryMb) {
+        this.memoryMb.set(memoryMb);
+        return this;
+    }
+
+    public LauncherBuilder withVCores(final long vCores) {
+        this.vCores.set(vCores);
+        return this;
+    }
+
+    public LauncherBuilder withQueue(final String queue) {
+        this.queue.set(queue);
+        return this;
+    }
+
+    public LauncherBuilder withSharelib(final String sharelib) {
+        this.sharelib.set(sharelib);
+        return this;
+    }
+
+    public LauncherBuilder withViewAcl(final String viewAcl) {
+        this.viewAcl.set(viewAcl);
+        return this;
+    }
+
+    public LauncherBuilder withModifyAcl(final String modifyAcl) {
+        this.modifyAcl.set(modifyAcl);
+        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/MapReduceAction.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/MapReduceAction.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/MapReduceAction.java
new file mode 100644
index 0000000..a90cc3f
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/MapReduceAction.java
@@ -0,0 +1,138 @@
+/**
+ * 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 map-reduce action.
+ * Instances of this class should be built using the builder {@link 
MapReduceActionBuilder}.
+ *
+ * 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 MapReduceActionBuilder#build} either.
+ */
[email protected]
[email protected]
+public class MapReduceAction extends Node implements HasAttributes {
+    private final ActionAttributes attributes;
+
+    MapReduceAction(final ConstructionData constructionData,
+                    final ActionAttributes attributes) {
+        super(constructionData);
+
+        this.attributes = attributes;
+    }
+
+    public String getResourceManager() {
+        return attributes.getResourceManager();
+    }
+
+    /**
+     * Returns the name node stored in this {@link MapReduceAction} object.
+     * @return The name node stored in this {@link MapReduceAction} object.
+     */
+    public String getNameNode() {
+        return attributes.getNameNode();
+    }
+
+    /**
+     * Returns the {@link Prepare} object stored in this {@link 
MapReduceAction} object.
+     * @return The {@link Prepare} object stored in this {@link 
MapReduceAction} object.
+     */
+    public Prepare getPrepare() {
+        return attributes.getPrepare();
+    }
+
+    /**
+     * Returns the {@link Streaming} object stored in this {@link 
MapReduceAction} object.
+     * @return The {@link Streaming} object stored in this {@link 
MapReduceAction} object.
+     */
+    public Streaming getStreaming() {
+        return attributes.getStreaming();
+    }
+
+    /**
+     * Returns the {@link Pipes} object stored in this {@link MapReduceAction} 
object.
+     * @return The {@link Pipes} object stored in this {@link MapReduceAction} 
object.
+     */
+    public Pipes getPipes() {
+        return attributes.getPipes();
+    }
+
+    /**
+     * Returns the list of job XMLs stored in this {@link MapReduceAction} 
object.
+     * @return The list of job XMLs stored in this {@link MapReduceAction} 
object.
+     */
+    public List<String> getJobXmls() {
+        return attributes.getJobXmls();
+    }
+
+
+    /**
+     * Returns the value associated with the provided configuration property 
name.
+     * @param property The name of the configuration property for which the 
value will be returned.
+     * @return The value associated with the provided configuration property 
name.
+     */
+    public String getConfigProperty(final String property) {
+        return attributes.getConfiguration().get(property);
+    }
+
+    /**
+     * Returns an immutable map of the configuration key-value pairs stored in 
this {@link MapReduceAction} object.
+     * @return An immutable map of the configuration key-value pairs stored in 
this {@link MapReduceAction} object.
+     */
+    public Map<String, String> getConfiguration() {
+        return attributes.getConfiguration();
+    }
+
+    /**
+     * Returns the configuration class property of this {@link 
MapReduceAction} object.
+     * @return The configuration class property of this {@link 
MapReduceAction} object.
+     */
+    public String getConfigClass() {
+        return attributes.getConfigClass();
+    }
+
+    /**
+     * Returns an immutable list of the names of the files associated with 
this {@link MapReduceAction} object.
+     * @return An immutable list of the names of the files associated with 
this {@link MapReduceAction} object.
+     */
+    public List<String> getFiles() {
+        return attributes.getFiles();
+    }
+
+    /**
+     * Returns an immutable list of the names of the archives associated with 
this {@link MapReduceAction} object.
+     * @return An immutable list of the names of the archives associated with 
this {@link MapReduceAction} object.
+     */
+    public List<String> getArchives() {
+        return attributes.getArchives();
+    }
+
+    public ActionAttributes getAttributes() {
+        return attributes;
+    }
+}

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/MapReduceActionBuilder.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/MapReduceActionBuilder.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/MapReduceActionBuilder.java
new file mode 100644
index 0000000..c8b3991
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/MapReduceActionBuilder.java
@@ -0,0 +1,256 @@
+/**
+ * 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 builder class for {@link MapReduceAction}.
+ * <p>
+ * 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.
+ * <p>
+ * Builder instances can be used to build several elements, although 
properties already set cannot be changed after
+ * a call to {@link MapReduceActionBuilder#build} either.
+ */
[email protected]
[email protected]
+public class MapReduceActionBuilder extends 
NodeBuilderBaseImpl<MapReduceActionBuilder> implements Builder<MapReduceAction> 
{
+    private final ActionAttributesBuilder attributesBuilder;
+
+    /**
+     * Creates and returns an empty builder.
+     * @return An empty builder.
+     */
+    public static MapReduceActionBuilder create() {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.create();
+
+        return new MapReduceActionBuilder(
+                null,
+                builder);
+    }
+
+    /**
+     * Create and return a new {@link MapReduceActionBuilder} that is based on 
an already built
+     * {@link MapReduceAction} object. The properties of the builder will 
initially be the same as those of the
+     * provided {@link MapReduceAction} object, but it is possible to modify 
them once.
+     * @param action The {@link MapReduceAction} object on which this {@link 
MapReduceActionBuilder} will be based.
+     * @return A new {@link MapReduceActionBuilder} that is based on a 
previously built {@link MapReduceAction} object.
+     */
+    public static MapReduceActionBuilder createFromExistingAction(final Node 
action) {
+        final ActionAttributesBuilder builder = 
ActionAttributesBuilder.createFromAction(action);
+
+        return new MapReduceActionBuilder(
+                action,
+                builder);
+    }
+
+    MapReduceActionBuilder(final Node action,
+                           final ActionAttributesBuilder attributesBuilder) {
+        super(action);
+
+        this.attributesBuilder = attributesBuilder;
+    }
+
+    public MapReduceActionBuilder withResourceManager(final String 
resourceManager) {
+        attributesBuilder.withResourceManager(resourceManager);
+        return this;
+    }
+
+    /**
+     * Registers a name node.
+     * @param nameNode The string representing the name node.
+     * @return this
+     * @throws IllegalStateException if a name node has already been set on 
this builder.
+     */
+    public MapReduceActionBuilder withNameNode(final String nameNode) {
+        attributesBuilder.withNameNode(nameNode);
+        return this;
+    }
+
+    /**
+     * Registers a {@link Prepare} object.
+     * @param prepare The {@link Prepare} object to register.
+     * @return this
+     * @throws IllegalStateException if a {@link Prepare} object has already 
been set on this builder.
+     */
+    public MapReduceActionBuilder withPrepare(final Prepare prepare) {
+        attributesBuilder.withPrepare(prepare);
+        return this;
+    }
+
+    /**
+     * Registers a {@link Streaming} object.
+     * @param streaming The {@link Streaming} object to register.
+     * @return this
+     * @throws IllegalStateException if a {@link Streaming} object has already 
been set on this builder.
+     */
+    public MapReduceActionBuilder withStreaming(final Streaming streaming) {
+        attributesBuilder.withStreaming(streaming);
+        return this;
+    }
+
+    /**
+     * Registers a {@link Pipes} object.
+     * @param pipes The {@link Pipes} object to register.
+     * @return this
+     * @throws IllegalStateException if a {@link Pipes} object has already 
been set on this builder.
+     */
+    public MapReduceActionBuilder withPipes(final Pipes pipes) {
+        attributesBuilder.withPipes(pipes);
+        return this;
+    }
+
+    /**
+     * Registers a job XML with this builder.
+     * @param jobXml The job XML to register.
+     * @return this
+     */
+    public MapReduceActionBuilder withJobXml(final String jobXml) {
+        attributesBuilder.withJobXml(jobXml);
+        return this;
+    }
+
+    /**
+     * Removes a job XML if it is registered with this builder, otherwise does 
nothing.
+     * @param jobXml The job XML to remove.
+     * @return this
+     */
+    public MapReduceActionBuilder withoutJobXml(final String jobXml) {
+        attributesBuilder.withoutJobXml(jobXml);
+        return this;
+    }
+
+    /**
+     * Removes all job XMLs that are registered with this builder.
+     * @return this
+     */
+    public MapReduceActionBuilder clearJobXmls() {
+        attributesBuilder.clearJobXmls();
+        return this;
+    }
+
+    /**
+     * Registers a configuration property (a key-value pair) with this 
builder. If the provided key has already been
+     * set on this builder, an exception is thrown. Setting a key to null 
means deleting it.
+     * @param key The name of the property to set.
+     * @param value The value of the property to set.
+     * @return this
+     * @throws IllegalStateException if the provided key has already been set 
on this builder.
+     */
+    public MapReduceActionBuilder withConfigProperty(final String key, final 
String value) {
+        attributesBuilder.withConfigProperty(key, value);
+        return this;
+    }
+
+    /**
+     * Registers a configuration class with this builder.
+     * @param configClass The string representing the configuration class.
+     * @return this
+     * @throws IllegalStateException if a configuration class has already been 
set on this builder.
+     */
+    public MapReduceActionBuilder withConfigClass(final String configClass) {
+        attributesBuilder.withConfigClass(configClass);
+        return this;
+    }
+
+    /**
+     * Registers a file with this builder.
+     * @param file The file to register.
+     * @return this
+     */
+    public MapReduceActionBuilder withFile(final String file) {
+        attributesBuilder.withFile(file);
+        return this;
+    }
+
+    /**
+     * Removes a file if it is registered with this builder, otherwise does 
nothing.
+     * @param file The file to remove.
+     * @return this
+     */
+    public MapReduceActionBuilder withoutFile(final String file) {
+        attributesBuilder.withoutFile(file);
+        return this;
+    }
+
+    /**
+     * Removes all files that are registered with this builder.
+     * @return this
+     */
+    public MapReduceActionBuilder clearFiles() {
+        attributesBuilder.clearFiles();
+        return this;
+    }
+
+    /**
+     * Registers an archive with this builder.
+     * @param archive The archive to register.
+     * @return this
+     */
+    public MapReduceActionBuilder withArchive(final String archive) {
+        attributesBuilder.withArchive(archive);
+        return this;
+    }
+
+    /**
+     * Removes an archive if it is registered with this builder, otherwise 
does nothing.
+     * @param archive The archive to remove.
+     * @return this
+     */
+    public MapReduceActionBuilder withoutArchive(final String archive) {
+        attributesBuilder.withoutArchive(archive);
+        return this;
+    }
+
+    /**
+     * Removes all archives that are registered with this builder.
+     * @return this
+     */
+    public MapReduceActionBuilder clearArchives() {
+        attributesBuilder.clearArchives();
+        return this;
+    }
+
+    /**
+     * Creates a new {@link MapReduceAction} object with the properties stores 
in this builder.
+     * The new {@link MapReduceAction} object is independent of this builder 
and the builder can be used to build
+     * new instances.
+     * @return A new {@link MapReduceAction} object with the properties stored 
in this builder.
+     */
+    @Override
+    public MapReduceAction build() {
+        final Node.ConstructionData constructionData = getConstructionData();
+
+        final MapReduceAction instance = new MapReduceAction(
+                constructionData,
+                attributesBuilder.build());
+
+        addAsChildToAllParents(instance);
+
+        return instance;
+    }
+
+    @Override
+    protected MapReduceActionBuilder getRuntimeSelfReference() {
+        return this;
+    }
+}
\ 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/Mkdir.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Mkdir.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Mkdir.java
new file mode 100644
index 0000000..f98cd72
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Mkdir.java
@@ -0,0 +1,48 @@
+/**
+ * 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 mkdir command of {@link FSAction} and the prepare 
section of other actions.
+ */
[email protected]
[email protected]
+public class Mkdir {
+    private final String path;
+
+    /**
+     * Creates a new {@link Mkdir} object with the provided path.
+     * @param path The path of the directory that will be created when this 
operation is run.
+     */
+    public Mkdir(final String path) {
+        this.path = path;
+    }
+
+    /**
+     * Returns the path of the directory that will be created.
+     * @return The path of the directory that will be created.
+     */
+    public String getPath() {
+        return path;
+    }
+
+}
\ 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/Move.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Move.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Move.java
new file mode 100644
index 0000000..489a8f9
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Move.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 org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+/**
+ * A class representing the move command of {@link FSAction}.
+ */
[email protected]
[email protected]
+public class Move {
+    private final String source;
+    private final String target;
+
+    /**
+     * Creates a new {@link Move} object with the provided path.
+     * @param source HDFS path
+     * @param target HDFS path
+     */
+    public Move(final String source, final String target) {
+        this.source = source;
+        this.target = target;
+    }
+
+    /**
+     * Returns the source path of this {@link Move} operation.
+     * @return The source path of this {@link Move} operation.
+     */
+    public String getSource() {
+        return source;
+    }
+
+    /**
+     * Returns the target path of this {@link Move} operation.
+     * @return The target path of this {@link Move} operation.
+     */
+    public String getTarget() {
+        return target;
+    }
+}

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/Node.java
----------------------------------------------------------------------
diff --git 
a/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Node.java
 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Node.java
new file mode 100644
index 0000000..2abdba9
--- /dev/null
+++ 
b/fluent-job/fluent-job-api/src/main/java/org/apache/oozie/fluentjob/api/action/Node.java
@@ -0,0 +1,282 @@
+/**
+ * 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.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.workflow.Credential;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * An abstract base class for API level action nodes. Concrete instances of 
the actions should be created using their
+ * respective builders that inherit from {@link NodeBuilderBaseImpl} and 
implement the {@link Builder} interface.
+ *
+ * The public interface of {@link Node} objects is immutable. This way we can 
ensure at compile time that there are no
+ * cycles in the graph: once a node is built, it cannot get new parents. On 
the other hand, {@link Node} objects still
+ * keep track of their children, which are necessarily added after the parent 
node is built, so  these objects are not
+ * really immutable and should not be used in a multithreaded environment 
without external synchronization.
+ */
[email protected]
[email protected]
+public abstract class Node {
+    private final Attributes attributes;
+    private final ImmutableList<Node> parentsWithoutConditions;
+    private final ImmutableList<Node.NodeWithCondition> parentsWithConditions;
+    private final ErrorHandler errorHandler;
+
+    private final List<Node> childrenWithoutConditions; // MUTABLE!
+    private final List<NodeWithCondition> childrenWithConditions; // MUTABLE!
+    private Node defaultConditionalChild; // MUTABLE!
+
+    Node(final ConstructionData constructionData) {
+        this(constructionData.attributes,
+             constructionData.parents,
+             constructionData.parentsWithConditions,
+             constructionData.errorHandler);
+    }
+
+    Node(final Attributes attributes,
+         final ImmutableList<Node> parentsWithoutConditions,
+         final ImmutableList<Node.NodeWithCondition> parentsWithConditions,
+         final ErrorHandler errorHandler)
+    {
+        this.attributes = attributes;
+        this.parentsWithoutConditions = parentsWithoutConditions;
+        this.parentsWithConditions = parentsWithConditions;
+        this.errorHandler = errorHandler;
+
+        this.childrenWithoutConditions = new ArrayList<>();
+        this.childrenWithConditions = new ArrayList<>();
+        this.defaultConditionalChild = null;
+    }
+
+    /**
+     * Returns the name of the node.
+     * @return The name of the node.
+     */
+    public String getName() {
+        return attributes.name;
+    }
+
+    /**
+     * Get the {@link Credential} associated with this {@link Node}.
+     * @return the {@link Credential} associated with this {@link Node}
+     */
+    public List<Credential> getCredentials() {
+        return attributes.credentials;
+    }
+
+    /**
+     * Get the {@code retry-max} attribute of this {@link Node}.
+     * @return the {@code retry-max}
+     */
+    public Integer getRetryMax() {
+        return attributes.retryMax;
+    }
+
+    /**
+     * Get the {@code retry-interval} attribute of this {@link Node}.
+     * @return the {@code retry-interval}
+     */
+    public Integer getRetryInterval() {
+        return attributes.retryInterval;
+    }
+
+    /**
+     * Get the {@code retry-policy} attribute of this {@link Node}.
+     * @return the {@code retry-policy}
+     */
+    public String getRetryPolicy() {
+        return attributes.retryPolicy;
+    }
+
+    /**
+     * Returns a list of all the parents of this node, including unconditional 
and conditional parents.
+     * @return A list of all the parents of this node object.
+     */
+    public List<Node> getAllParents() {
+        final List<Node> allParents = new 
ArrayList<>(parentsWithoutConditions);
+
+        for (final NodeWithCondition parentWithCondition : 
parentsWithConditions) {
+            allParents.add(parentWithCondition.getNode());
+        }
+
+        return Collections.unmodifiableList(allParents);
+    }
+
+    /**
+     * Returns a list of the unconditional parents of this node.
+     * @return A list of the unconditional parents of this node.
+     */
+    public List<Node> getParentsWithoutConditions() {
+        return parentsWithoutConditions;
+    }
+
+    /**
+     * Returns a list of the conditional parents of this node together with 
their conditions.
+     * @return A list of the conditional parents of this node together with 
their conditions.
+     */
+    public List<Node.NodeWithCondition> getParentsWithConditions() {
+        return parentsWithConditions;
+    }
+
+    /**
+     * Returns the error handler of this node.
+     * @return The error handler of this node.
+     */
+    public ErrorHandler getErrorHandler() {
+        return errorHandler;
+    }
+
+    void addChild(final Node child) {
+        Preconditions.checkState(childrenWithConditions.isEmpty(),
+                "Trying to add a child without condition to a node that 
already has at least one child with a condition.");
+
+        this.childrenWithoutConditions.add(child);
+    }
+
+    void addChildWithCondition(final Node child, final String condition) {
+        Preconditions.checkState(childrenWithoutConditions.isEmpty(),
+                "Trying to add a child with condition to a node that already 
has at least one child without a condition.");
+
+        this.childrenWithConditions.add(new NodeWithCondition(child, 
Condition.actualCondition(condition)));
+    }
+
+    void addChildAsDefaultConditional(final Node child) {
+        Preconditions.checkState(childrenWithoutConditions.isEmpty(),
+                "Trying to add a default conditional child to a node that 
already has at least one child without a condition.");
+
+        Preconditions.checkState(defaultConditionalChild == null,
+                "Trying to add a default conditional child to a node that 
already has one.");
+
+        this.defaultConditionalChild = child;
+    }
+
+    /**
+     * Returns an unmodifiable view of list of all the children of this {@link 
Node}.
+     * @return An unmodifiable view of list of all the children of this {@link 
Node}.
+     */
+    public List<Node> getAllChildren() {
+        final List<Node> allChildren = new 
ArrayList<>(childrenWithoutConditions);
+
+        for (final NodeWithCondition nodeWithCondition : 
getChildrenWithConditions()) {
+            allChildren.add(nodeWithCondition.getNode());
+        }
+
+        return Collections.unmodifiableList(allChildren);
+    }
+
+    /**
+     * Returns an unmodifiable view of list of the children without condition 
of this {@link Node}.
+     * @return An unmodifiable view of list of the children without condition 
of this {@link Node}.
+     */
+    public List<Node> getChildrenWithoutConditions() {
+        return Collections.unmodifiableList(childrenWithoutConditions);
+    }
+
+    /**
+     * Returns an unmodifiable view of list of the children with condition 
(including the default) of this {@link Node}.
+     * @return An unmodifiable view of list of the children with condition 
(including the default) of this {@link Node}.
+     */
+    public List<NodeWithCondition> getChildrenWithConditions() {
+        if (defaultConditionalChild == null) {
+            return Collections.unmodifiableList(childrenWithConditions);
+        }
+
+        final List<NodeWithCondition> results = new 
ArrayList<>(childrenWithConditions);
+        results.add(new NodeWithCondition(defaultConditionalChild, 
Condition.defaultCondition()));
+
+        return Collections.unmodifiableList(results);
+    }
+
+    /**
+     * Returns the default conditional child of this {@link Node}.
+     * @return The default conditional child of this {@link Node}.
+     */
+    public Node getDefaultConditionalChild() {
+        return defaultConditionalChild;
+    }
+
+    public static class NodeWithCondition {
+        private final Node node;
+        private final Condition condition;
+
+        public NodeWithCondition(final Node node,
+                                 final Condition condition) {
+            this.node = node;
+            this.condition = condition;
+        }
+
+        public Node getNode() {
+            return node;
+        }
+
+        public Condition getCondition() {
+            return condition;
+        }
+    }
+
+    static class ConstructionData {
+
+        ConstructionData(final Attributes attributes,
+                         final ImmutableList<Node> parents,
+                         final ImmutableList<NodeWithCondition> 
parentsWithConditions,
+                         final ErrorHandler errorHandler) {
+            this.attributes = attributes;
+            this.parents = parents;
+            this.parentsWithConditions = parentsWithConditions;
+            this.errorHandler = errorHandler;
+        }
+
+        private final Attributes attributes;
+        private final ImmutableList<Node> parents;
+        private final ImmutableList<NodeWithCondition> parentsWithConditions;
+        private final ErrorHandler errorHandler;
+    }
+
+    static class Attributes {
+        private final String name;
+        private final ImmutableList<Credential> credentials;
+        private final Integer retryMax;
+        private final Integer retryInterval;
+        private final String retryPolicy;
+
+        Attributes(final String name) {
+            this(name, ImmutableList.of(), null, null, null);
+        }
+
+        Attributes(final String name,
+                   final List<Credential> credentials,
+                   final Integer retryMax,
+                   final Integer retryInterval,
+                   final String retryPolicy) {
+            this.name = name;
+            this.credentials = ImmutableList.copyOf(credentials);
+            this.retryMax = retryMax;
+            this.retryInterval = retryInterval;
+            this.retryPolicy = retryPolicy;
+        }
+    }
+}

Reply via email to