You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by db...@apache.org on 2016/10/27 19:04:23 UTC
[10/10] ambari git commit: AMBARI-18691. Improve and Update Workflow
designer to support coordinators and bundles. (Belliraj HB via dipayanb)
AMBARI-18691. Improve and Update Workflow designer to support coordinators and bundles. (Belliraj HB via dipayanb)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/7c7412ed
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/7c7412ed
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/7c7412ed
Branch: refs/heads/branch-2.5
Commit: 7c7412ed4c350d900086cd880f1e4a5fdffc569b
Parents: 7306e0b
Author: Dipayan Bhowmick <di...@gmail.com>
Authored: Fri Oct 28 00:33:36 2016 +0530
Committer: Dipayan Bhowmick <di...@gmail.com>
Committed: Fri Oct 28 00:33:36 2016 +0530
----------------------------------------------------------------------
.../apache/oozie/ambari/view/HDFSFileUtils.java | 87 +
.../org/apache/oozie/ambari/view/JobType.java | 22 +
.../ambari/view/OozieProxyImpersonator.java | 1042 ++++++------
.../apache/oozie/ambari/view/OozieUtils.java | 71 +
.../org/apache/oozie/ambari/view/Utils.java | 154 ++
.../wfmanager/src/main/resources/ui/.jshintrc | 38 +
.../main/resources/ui/app/components/.gitkeep | 0
.../ui/app/components/archive-config.js | 3 +-
.../ui/app/components/bundle-config.js | 262 +++
.../ui/app/components/bundle-coord-config.js | 108 ++
.../ui/app/components/conditional-data-input.js | 78 +
.../ui/app/components/confirmation-dialog.js | 25 +
.../resources/ui/app/components/coord-config.js | 521 ++++++
.../ui/app/components/credentials-config.js | 175 +-
.../app/components/data-input-output-config.js | 97 ++
.../resources/ui/app/components/data-input.js | 41 +
.../ui/app/components/dataset-config.js | 103 ++
.../ui/app/components/date-with-expr.js | 78 +
.../ui/app/components/decision-add-branch.js | 78 +-
.../ui/app/components/decision-config.js | 43 +-
.../ui/app/components/designer-workspace.js | 158 ++
.../ui/app/components/distcp-action-info.js | 26 +
.../ui/app/components/distcp-action.js | 6 +-
.../ui/app/components/email-action-info.js | 26 +
.../resources/ui/app/components/email-action.js | 33 +-
.../resources/ui/app/components/file-config.js | 3 +-
.../ui/app/components/flow-designer.js | 728 +++++----
.../ui/app/components/fs-action-info.js | 26 +
.../resources/ui/app/components/fs-action.js | 64 +-
.../ui/app/components/fsaction-info.js | 21 +
.../resources/ui/app/components/hdfs-browser.js | 5 +-
.../ui/app/components/hive-action-info.js | 26 +
.../resources/ui/app/components/hive-action.js | 38 +-
.../ui/app/components/hive2-action-info.js | 25 +
.../resources/ui/app/components/hive2-action.js | 44 +-
.../resources/ui/app/components/info-header.js | 26 +
.../ui/app/components/instance-list-config.js | 54 +
.../ui/app/components/java-action-info.js | 25 +
.../resources/ui/app/components/java-action.js | 25 +-
.../resources/ui/app/components/job-config.js | 303 ++++
.../resources/ui/app/components/job-details.js | 391 ++++-
.../ui/app/components/killnode-config.js | 29 +
.../ui/app/components/killnode-manager.js | 62 +
.../ui/app/components/map-red-action.js | 6 +-
.../ui/app/components/map-reduce-action-info.js | 25 +
.../ui/app/components/name-value-config.js | 7 +-
.../ui/app/components/name-value-info.js | 21 +
.../ui/app/components/named-properties.js | 25 +-
.../ui/app/components/pig-action-info.js | 25 +
.../resources/ui/app/components/pig-action.js | 16 +-
.../ui/app/components/prepare-config-fs.js | 7 +-
.../ui/app/components/prepare-config-info.js | 21 +
.../ui/app/components/prepare-config.js | 3 +-
.../ui/app/components/preview-dialog.js | 20 +
.../ui/app/components/property-value-config.js | 21 +
.../main/resources/ui/app/components/save-wf.js | 170 ++
.../ui/app/components/shell-action-info.js | 25 +
.../resources/ui/app/components/shell-action.js | 20 +-
.../resources/ui/app/components/sla-info.js | 148 +-
.../ui/app/components/spark-action-info.js | 25 +
.../resources/ui/app/components/spark-action.js | 59 +-
.../ui/app/components/sqoop-action-info.js | 25 +
.../resources/ui/app/components/sqoop-action.js | 5 +-
.../ui/app/components/ssh-action-info.js | 25 +
.../resources/ui/app/components/ssh-action.js | 24 +-
.../app/components/sub-workflow-action-info.js | 25 +
.../resources/ui/app/components/sub-workflow.js | 17 +-
.../ui/app/components/transition-config.js | 44 +-
.../ui/app/components/workflow-action-editor.js | 58 +-
.../ui/app/components/workflow-actions.js | 7 +
.../ui/app/components/workflow-credentials.js | 65 +-
.../app/components/workflow-job-action-info.js | 22 +
.../ui/app/components/workflow-node.js | 9 +
.../ui/app/components/workflow-parameters.js | 64 +-
.../resources/ui/app/components/workflow-sla.js | 15 +-
.../main/resources/ui/app/controllers/.gitkeep | 0
.../main/resources/ui/app/controllers/design.js | 5 -
.../ui/app/domain/action-type-resolver.js | 62 +
.../ui/app/domain/actionjob_hanlder.js | 1 +
.../app/domain/bundle/bundle-xml-generator.js | 55 +
.../ui/app/domain/bundle/bundle-xml-importer.js | 87 +
.../resources/ui/app/domain/bundle/bundle.js | 22 +
.../coordinator/coordinator-xml-generator.js | 204 +++
.../coordinator/coordinator-xml-importer.js | 272 ++++
.../ui/app/domain/coordinator/coordinator.js | 22 +
.../ui/app/domain/cytoscape-flow-renderer.js | 348 ++++
.../resources/ui/app/domain/cytoscape-style.js | 123 ++
.../ui/app/domain/default-layout-manager.js | 10 +-
.../resources/ui/app/domain/findnode-mixin.js | 113 +-
.../src/main/resources/ui/app/domain/id-gen.js | 4 +
.../ui/app/domain/jsplumb-flow-renderer.js | 194 +++
.../resources/ui/app/domain/mapping-utils.js | 31 +-
.../resources/ui/app/domain/node-factory.js | 10 +-
.../resources/ui/app/domain/node-handler.js | 49 +-
.../src/main/resources/ui/app/domain/node.js | 3 +-
.../main/resources/ui/app/domain/sla-info.js | 38 +-
.../main/resources/ui/app/domain/transition.js | 5 +-
.../ui/app/domain/workflow-importer.js | 8 +-
.../ui/app/domain/workflow-json-importer.js | 92 ++
.../ui/app/domain/workflow-path-util.js | 73 +
.../ui/app/domain/workflow-xml-generator.js | 1 -
.../main/resources/ui/app/domain/workflow.js | 114 +-
.../ui/app/domain/workflow_xml_mapper.js | 5 +-
.../src/main/resources/ui/app/helpers/.gitkeep | 0
.../src/main/resources/ui/app/index.html | 9 +
.../src/main/resources/ui/app/routes/.gitkeep | 0
.../main/resources/ui/app/routes/dashboard.js | 10 +-
.../src/main/resources/ui/app/routes/design.js | 38 +-
.../ui/app/services/workflow-clipboard.js | 34 +
.../ui/app/services/workspace-manager.js | 62 +
.../src/main/resources/ui/app/styles/app.less | 1497 ++++++++++++++++++
.../ui/app/templates/components/.gitkeep | 0
.../app/templates/components/bundle-config.hbs | 129 ++
.../components/bundle-coord-config.hbs | 58 +
.../templates/components/bundle-job-details.hbs | 17 +-
.../components/conditional-data-input.hbs | 64 +
.../components/confirmation-dialog.hbs | 34 +
.../app/templates/components/coord-config.hbs | 352 ++++
.../templates/components/coord-job-details.hbs | 17 +-
.../templates/components/credentials-config.hbs | 33 +-
.../components/data-input-output-config.hbs | 68 +
.../ui/app/templates/components/data-input.hbs | 40 +
.../app/templates/components/dataset-config.hbs | 70 +
.../app/templates/components/date-with-expr.hbs | 41 +
.../components/decision-add-branch.hbs | 4 +-
.../templates/components/decision-config.hbs | 2 +-
.../templates/components/designer-workspace.hbs | 106 ++
.../templates/components/distcp-action-info.hbs | 35 +
.../templates/components/email-action-info.hbs | 28 +
.../app/templates/components/email-action.hbs | 10 +-
.../ui/app/templates/components/field-error.hbs | 3 +
.../app/templates/components/flow-designer.hbs | 300 ++--
.../app/templates/components/fs-action-info.hbs | 33 +
.../ui/app/templates/components/fs-action.hbs | 3 +-
.../app/templates/components/fsaction-info.hbs | 39 +
.../app/templates/components/hdfs-browser.hbs | 2 +-
.../templates/components/hive-action-info.hbs | 47 +
.../ui/app/templates/components/hive-action.hbs | 6 +-
.../templates/components/hive2-action-info.hbs | 49 +
.../app/templates/components/hive2-action.hbs | 6 +-
.../ui/app/templates/components/info-header.hbs | 18 +
.../components/instance-list-config.hbs | 35 +
.../templates/components/java-action-info.hbs | 52 +
.../ui/app/templates/components/java-action.hbs | 4 +-
.../ui/app/templates/components/job-config.hbs | 126 ++
.../ui/app/templates/components/job-details.hbs | 2 +-
.../templates/components/killnode-config.hbs | 67 +
.../templates/components/killnode-manager.hbs | 69 +
.../components/map-reduce-action-info.hbs | 41 +
.../templates/components/name-value-info.hbs | 22 +
.../templates/components/named-properties.hbs | 2 +-
.../templates/components/pig-action-info.hbs | 47 +
.../ui/app/templates/components/pig-action.hbs | 2 +-
.../templates/components/prepare-config-fs.hbs | 46 +-
.../components/prepare-config-info.hbs | 22 +
.../app/templates/components/preview-dialog.hbs | 33 +
.../components/property-value-config.hbs | 22 +
.../ui/app/templates/components/save-wf.hbs | 79 +
.../templates/components/shell-action-info.hbs | 48 +
.../app/templates/components/shell-action.hbs | 4 +-
.../ui/app/templates/components/sla-info.hbs | 17 +-
.../templates/components/spark-action-info.hbs | 46 +
.../app/templates/components/spark-action.hbs | 6 +-
.../templates/components/sqoop-action-info.hbs | 41 +
.../templates/components/ssh-action-info.hbs | 32 +
.../ui/app/templates/components/ssh-action.hbs | 4 +-
.../components/sub-workflow-action-info.hbs | 29 +
.../app/templates/components/sub-workflow.hbs | 2 +-
.../templates/components/transition-config.hbs | 23 +-
.../templates/components/version-settings.hbs | 2 +-
.../templates/components/workflow-actions.hbs | 5 +
.../templates/components/workflow-config.hbs | 2 +-
.../components/workflow-credentials.hbs | 34 +-
.../components/workflow-job-action-info.hbs | 80 +
.../components/workflow-job-details.hbs | 158 +-
.../app/templates/components/workflow-node.hbs | 3 +-
.../components/workflow-parameters.hbs | 23 +-
.../resources/ui/app/templates/dashboard.hbs | 2 +-
.../main/resources/ui/app/templates/design.hbs | 2 +-
.../main/resources/ui/app/utils/constants.js | 43 +-
.../app/validators/decission-node-validator.js | 58 +
.../app/validators/duplicate-data-node-name.js | 60 +
.../validators/duplicate-flattened-node-name.js | 66 +
.../app/validators/duplicate-kill-node-name.js | 58 +
.../ui/app/validators/fs-action-validator.js | 76 +
.../ui/app/validators/job-params-validator.js | 54 +
.../ui/app/validators/operand-length.js | 46 +
.../resources/ui/app/validators/unique-name.js | 59 +
.../wfmanager/src/main/resources/ui/bower.json | 5 +-
.../src/main/resources/ui/ember-cli-build.js | 10 +
.../hdfs-directory-viewer/addon/.gitkeep | 0
.../hdfs-directory-viewer/app/.gitkeep | 0
.../tests/dummy/app/components/.gitkeep | 0
.../tests/dummy/app/controllers/.gitkeep | 0
.../tests/dummy/app/helpers/.gitkeep | 0
.../tests/dummy/app/models/.gitkeep | 0
.../tests/dummy/app/routes/.gitkeep | 0
.../dummy/app/templates/components/.gitkeep | 0
.../tests/integration/.gitkeep | 0
.../hdfs-directory-viewer/tests/unit/.gitkeep | 0
.../hdfs-directory-viewer/vendor/.gitkeep | 0
.../resources/ui/mock-service/mock-server.js | 52 +
.../main/resources/ui/mock-service/mockData.js | 316 ++++
.../src/main/resources/ui/package.json | 18 +-
.../main/resources/ui/public/assets/favicon.ico | Bin 0 -> 1150 bytes
.../main/resources/ui/public/assets/join.png | Bin 0 -> 331 bytes
.../main/resources/ui/public/assets/logo.png | Bin 0 -> 4568 bytes
.../main/resources/ui/public/assets/play.png | Bin 0 -> 1164 bytes
.../main/resources/ui/public/assets/sitemap.png | Bin 0 -> 317 bytes
.../main/resources/ui/public/assets/stop.png | Bin 0 -> 1164 bytes
.../src/main/resources/ui/public/loader.gif | Bin 0 -> 42435 bytes
.../resources/ui/public/sampledata/bundle.xml | 32 +
.../ui/public/sampledata/coordinator.xml | 113 ++
.../resources/ui/tests/integration/.gitkeep | 0
.../components/bundle-config-test.js | 40 +
.../components/bundle-coord-config-test.js | 40 +
.../components/conditional-data-input-test.js | 40 +
.../components/confirmation-dialog-test.js | 40 +
.../integration/components/coord-config-test.js | 40 +
.../components/data-input-output-config-test.js | 40 +
.../integration/components/data-input-test.js | 40 +
.../components/dataset-config-test.js | 40 +
.../components/date-with-expr-test.js | 40 +
.../components/designer-workspace-test.js | 40 +
.../components/distcp-action-info-test.js | 41 +
.../components/email-action-info-test.js | 41 +
.../components/fs-action-info-test.js | 41 +
.../components/fsaction-info-test.js | 41 +
.../components/hive-action-info-test.js | 41 +
.../components/hive2-action-info-test.js | 41 +
.../integration/components/info-header-test.js | 41 +
.../components/instance-list-config-test.js | 41 +
.../components/java-action-info-test.js | 41 +
.../integration/components/job-config-test.js | 41 +
.../components/killnode-config-test.js | 41 +
.../components/killnode-manager-test.js | 41 +
.../components/map-reduce-action-info-test.js | 41 +
.../components/name-value-info-test.js | 41 +
.../components/pig-action-info-test.js | 41 +
.../components/prepare-config-info-test.js | 41 +
.../components/preview-dialog-test.js | 40 +
.../components/property-value-config-test.js | 41 +
.../integration/components/save-wf-test.js | 40 +
.../components/shell-action-info-test.js | 41 +
.../components/spark-action-info-test.js | 41 +
.../components/sqoop-action-info-test.js | 41 +
.../components/ssh-action-info-test.js | 41 +
.../components/sub-workflow-action-info-test.js | 41 +
.../src/main/resources/ui/tests/unit/.gitkeep | 0
.../unit/services/workflow-clipboard-test.js | 29 +
.../unit/services/workspace-manager-test.js | 29 +
.../validators/decission-node-validator-test.js | 26 +
.../validators/duplicate-data-node-name-test.js | 27 +
.../duplicate-flattened-node-name-test.js | 27 +
.../validators/duplicate-kill-node-name-test.js | 27 +
.../unit/validators/fs-action-validator-test.js | 26 +
.../validators/job-params-validator-test.js | 26 +
.../unit/validators/operand-length-test.js | 27 +
.../tests/unit/validators/unique-name-test.js | 27 +
259 files changed, 13407 insertions(+), 1980 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/7c7412ed/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/HDFSFileUtils.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/HDFSFileUtils.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/HDFSFileUtils.java
new file mode 100644
index 0000000..58c3980
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/HDFSFileUtils.java
@@ -0,0 +1,87 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.ambari.view;
+
+import java.io.IOException;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.utils.hdfs.HdfsApi;
+import org.apache.ambari.view.utils.hdfs.HdfsUtil;
+import org.apache.hadoop.fs.FSDataInputStream;
+import org.apache.hadoop.fs.FSDataOutputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class HDFSFileUtils {
+ private final static Logger LOGGER = LoggerFactory
+ .getLogger(HDFSFileUtils.class);
+ private ViewContext viewContext;
+
+ public HDFSFileUtils(ViewContext viewContext) {
+ super();
+ this.viewContext = viewContext;
+ }
+ public boolean fileExists(String path) {
+ boolean fileExists;
+ try {
+ fileExists = getHdfsgetApi().exists(path);
+ } catch (IOException e) {
+ LOGGER.error(e.getMessage(), e);
+ throw new RuntimeException(e);
+ } catch (InterruptedException e) {
+ LOGGER.error(e.getMessage(), e);
+ throw new RuntimeException(e);
+ }
+ LOGGER.info("FILE exists for [" + path + "] returned [" + fileExists
+ + "]");
+ return fileExists;
+ }
+ public FSDataInputStream read(String filePath)throws IOException{
+ FSDataInputStream is;
+ try {
+ is = getHdfsgetApi().open(filePath);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ return is;
+ }
+ public String createWorkflowFile( String workflowFile,String postBody,
+ boolean overwrite) throws IOException {
+ FSDataOutputStream fsOut;
+ try {
+ fsOut = getHdfsgetApi().create(workflowFile,
+ overwrite);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ fsOut.write(postBody.getBytes());
+ fsOut.close();
+ return workflowFile;
+ }
+ private HdfsApi getHdfsgetApi() {
+ try {
+ return HdfsUtil.connectToHDFSApi(viewContext);
+ } catch (Exception ex) {
+ LOGGER.error("Error in getting HDFS Api", ex);
+ throw new RuntimeException(
+ "HdfsApi connection failed. Check \"webhdfs.url\" property",
+ ex);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/7c7412ed/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/JobType.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/JobType.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/JobType.java
new file mode 100644
index 0000000..5a47b68
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/JobType.java
@@ -0,0 +1,22 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.ambari.view;
+
+public enum JobType {
+ WORKFLOW, COORDINATOR, BUNDLE
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/7c7412ed/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/OozieProxyImpersonator.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/OozieProxyImpersonator.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/OozieProxyImpersonator.java
index 3ed6352..0533f04 100644
--- a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/OozieProxyImpersonator.java
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/OozieProxyImpersonator.java
@@ -20,14 +20,10 @@ package org.apache.oozie.ambari.view;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.StringReader;
-import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
@@ -48,522 +44,550 @@ import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import javax.ws.rs.core.UriInfo;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.TransformerFactoryConfigurationError;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
import org.apache.ambari.view.URLStreamProvider;
import org.apache.ambari.view.ViewContext;
-import org.apache.ambari.view.utils.ambari.AmbariApi;
-import org.apache.ambari.view.utils.hdfs.HdfsApi;
-import org.apache.ambari.view.utils.hdfs.HdfsUtil;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.hadoop.fs.FSDataInputStream;
-import org.apache.hadoop.fs.FSDataOutputStream;
+import org.apache.hadoop.security.AccessControlException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
+
+import com.google.inject.Singleton;
/**
* This is a class used to bridge the communication between the and the Oozie
* API executing inside ambari.
*/
+@Singleton
public class OozieProxyImpersonator {
-
- private static final String OOZIE_WF_APPLICATION_PATH_CONF_KEY = "oozie.wf.application.path";
- private static final String OOZIE_WF_RERUN_FAILNODES_CONF_KEY = "oozie.wf.rerun.failnodes";
- private static final String OOZIE_USE_SYSTEM_LIBPATH_CONF_KEY = "oozie.use.system.libpath";
- private static final String XML_INDENT_SPACES = "4";
- private static final String XML_INDENT_AMT_PROP_NAME = "{http://xml.apache.org/xslt}indent-amount";
- private ViewContext viewContext;
- private AmbariApi ambariApi;
- private HdfsApi _hdfsApi = null;
-
- private static final String USER_NAME_HEADER = "user.name";
- private static final String USER_OOZIE_SUPER = "oozie";
- private static final String DO_AS_HEADER = "doAs";
-
- private static final String SERVICE_URI_PROP = "oozie.service.uri";
- private static final String DEFAULT_SERVICE_URI = "http://sandbox.hortonworks.com:11000/oozie";
-
- private final static Logger LOGGER = LoggerFactory
- .getLogger(OozieProxyImpersonator.class);
-
- @Inject
- public OozieProxyImpersonator(ViewContext viewContext) {
- this.viewContext = viewContext;
- this.ambariApi = new AmbariApi(viewContext);
- LOGGER.info(String.format(
- "OozieProxyImpersonator initialized for instance: %s",
- viewContext.getInstanceName()));
- }
-
- @Path("/fileServices")
- public FileServices fileServices() {
- return new FileServices(viewContext);
- }
-
- @POST
- @Path("/submitWorkflow")
- @Consumes({MediaType.TEXT_PLAIN + "," + MediaType.TEXT_XML})
- public Response submitWorkflow(String postBody, @Context HttpHeaders headers,
- @Context UriInfo ui, @QueryParam("app.path") String appPath,
- @DefaultValue("false") @QueryParam("overwrite") Boolean overwrite) {
- LOGGER.info("submit workflow job called");
- try {
- if (StringUtils.isEmpty(appPath)) {
- throw new RuntimeException("app path can't be empty.");
- }
- appPath = appPath.trim();
- if (!overwrite) {
- boolean fileExists = getHdfsgetApi().exists(appPath);
- LOGGER.info("FILE exists for [" + appPath + "] returned [" + fileExists
- + "]");
- if (fileExists) {
- HashMap<String, String> resp = new HashMap<String, String>();
- resp.put("status", "workflow.folder.exists");
- resp.put("message", "Workflow Folder exists");
- return Response.status(Response.Status.BAD_REQUEST).entity(resp)
- .build();
- }
- }
- String workflowFile = null;
- if (appPath.endsWith(".xml")) {
- workflowFile = appPath;
- } else {
- workflowFile = appPath + (appPath.endsWith("/") ? "" : "/")
- + "workflow.xml";
- }
- postBody = formatXml(postBody);
- try {
- String filePath = createWorkflowFile(postBody, workflowFile, overwrite);
- LOGGER.info(String.format("submit workflow job done. filePath=[%s]",
- filePath));
- } catch (org.apache.hadoop.security.AccessControlException ace) {
- HashMap<String, String> resp = new HashMap<String, String>();
- resp.put("status", "workflow.oozie.error");
- resp.put("message", "You dont seem to have access to folder path.");
- return Response.status(Response.Status.BAD_REQUEST).entity(resp)
- .build();
- }
-
- String response = submitWorkflowJobToOozie(headers, appPath,
- ui.getQueryParameters());
- if (response != null && response.trim().startsWith("{")) {
- // dealing with oozie giving error but with 200 response.
- return Response.status(Response.Status.OK).entity(response).build();
- } else {
- HashMap<String, String> resp = new HashMap<String, String>();
- resp.put("status", "workflow.oozie.error");
- resp.put("message", response);
- return Response.status(Response.Status.BAD_REQUEST).entity(resp)
- .build();
- }
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- } catch (Exception e) {
- LOGGER.error("Error in submit workflow", e);
- throw new RuntimeException(e);
- }
- }
-
- @GET
- @Path("/readWorkflowXml")
- public Response readWorkflowXxml(
- @QueryParam("workflowXmlPath") String workflowPath) {
- if (StringUtils.isEmpty(workflowPath)) {
- throw new RuntimeException("workflowXmlPath can't be empty.");
- }
- try {
- final FSDataInputStream is = getHdfsgetApi().open(workflowPath);
- StreamingOutput streamer = new StreamingOutput() {
-
- @Override
- public void write(OutputStream os) throws IOException,
- WebApplicationException {
- IOUtils.copy(is, os);
- is.close();
- os.close();
- }
- };
- return Response.ok(streamer).status(200).build();
- } catch (org.apache.hadoop.security.AccessControlException ace) {
- HashMap<String, String> resp = new HashMap<String, String>();
- resp.put("status", "workflow.oozie.error");
- resp.put("message", "Access denied to file path");
- return Response.status(Response.Status.FORBIDDEN).entity(resp).build();
- } catch (IOException e) {
- LOGGER.error("Error in read worfklow file", e);
- throw new RuntimeException(e);
- } catch (InterruptedException e) {
- LOGGER.error("Error in read worfklow file", e);
- throw new RuntimeException(e);
- }
- }
-
- @GET
- @Path("/getDag")
- @Produces("image/png")
- public Response submitWorkflow(@Context HttpHeaders headers,
- @Context UriInfo ui, @QueryParam("jobid") String jobid) {
- String imgUrl = getServiceUri() + "/v2/job/" + jobid + "?show=graph";
- Map<String, String> newHeaders = getHeaders(headers);
- final InputStream is = readFromOozie(headers, imgUrl, HttpMethod.GET, null,
- newHeaders);
- StreamingOutput streamer = new StreamingOutput() {
-
- @Override
- public void write(OutputStream os) throws IOException,
- WebApplicationException {
- IOUtils.copy(is, os);
- is.close();
- os.close();
- }
-
- };
- return Response.ok(streamer).status(200).build();
- }
-
- @GET
- @Path("/{path: .*}")
- public Response handleGet(@Context HttpHeaders headers, @Context UriInfo ui) {
- try {
- String serviceURI = buildURI(ui);
- return consumeService(headers, serviceURI, HttpMethod.GET, null);
- } catch (Exception ex) {
- LOGGER.error("Error in GET proxy", ex);
- return Response.status(Response.Status.BAD_REQUEST).entity(ex.toString())
- .build();
- }
- }
-
- @POST
- @Path("/{path: .*}")
- public Response handlePost(String xml, @Context HttpHeaders headers, @Context UriInfo ui) {
- try {
- String serviceURI = buildURI(ui);
- return consumeService(headers, serviceURI, HttpMethod.POST, xml);
- } catch (Exception ex) {
- LOGGER.error("Error in POST proxy", ex);
- return Response.status(Response.Status.BAD_REQUEST).entity(ex.toString())
- .build();
- }
- }
-
- @DELETE
- @Path("/{path: .*}")
- public Response handleDelete(@Context HttpHeaders headers, @Context UriInfo ui) {
- try {
- String serviceURI = buildURI(ui);
- return consumeService(headers, serviceURI, HttpMethod.POST, null);
- } catch (Exception ex) {
- LOGGER.error("Error in DELETE proxy", ex);
- return Response.status(Response.Status.BAD_REQUEST).entity(ex.toString())
- .build();
- }
- }
-
- @PUT
- @Path("/{path: .*}")
- public Response handlePut(String body, @Context HttpHeaders headers, @Context UriInfo ui) {
-
- try {
- String serviceURI = buildURI(ui);
- return consumeService(headers, serviceURI, HttpMethod.PUT, body);
- } catch (Exception ex) {
- LOGGER.error("Error in PUT proxy", ex);
- return Response.status(Response.Status.BAD_REQUEST).entity(ex.toString())
- .build();
- }
- }
-
- private String submitWorkflowJobToOozie(HttpHeaders headers, String filePath,
- MultivaluedMap<String, String> queryParams) {
- String nameNode = "hdfs://" + viewContext.getCluster().getConfigurationValue("hdfs-site", "dfs.namenode.rpc-address");
-
- if (!queryParams.containsKey("config.nameNode")) {
- ArrayList<String> nameNodes = new ArrayList<String>();
- LOGGER.info("Namenode===" + nameNode);
- nameNodes.add(nameNode);
- queryParams.put("config.nameNode", nameNodes);
- }
-
- HashMap<String, String> workflowConigs = new HashMap<String, String>();
- if (queryParams.containsKey("resourceManager")
- && "useDefault".equals(queryParams.getFirst("resourceManager"))) {
- String jobTrackerNode = viewContext.getCluster().getConfigurationValue(
- "yarn-site", "yarn.resourcemanager.address");
- LOGGER.info("jobTrackerNode===" + jobTrackerNode);
- workflowConigs.put("resourceManager", jobTrackerNode);
- workflowConigs.put("jobTracker", jobTrackerNode);
- }
- if (queryParams != null) {
- for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {
- if (entry.getKey().startsWith("config.")) {
- if (entry.getValue() != null && entry.getValue().size() > 0) {
- workflowConigs.put(entry.getKey().substring(7), entry.getValue()
- .get(0));
- }
- }
- }
- }
-
- if (queryParams.containsKey("oozieconfig.useSystemLibPath")) {
- String useSystemLibPath = queryParams
- .getFirst("oozieconfig.useSystemLibPath");
- workflowConigs.put(OOZIE_USE_SYSTEM_LIBPATH_CONF_KEY, useSystemLibPath);
- } else {
- workflowConigs.put(OOZIE_USE_SYSTEM_LIBPATH_CONF_KEY, "true");
- }
- if (queryParams.containsKey("oozieconfig.rerunOnFailure")) {
- String rerunFailnodes = queryParams
- .getFirst("oozieconfig.rerunOnFailure");
- workflowConigs.put(OOZIE_WF_RERUN_FAILNODES_CONF_KEY, rerunFailnodes);
- } else {
- workflowConigs.put(OOZIE_WF_RERUN_FAILNODES_CONF_KEY, "true");
- }
-
- workflowConigs.put("user.name", viewContext.getUsername());
- workflowConigs.put(OOZIE_WF_APPLICATION_PATH_CONF_KEY, nameNode + filePath);
- String configXMl = generateConigXml(workflowConigs);
- LOGGER.info("Config xml==" + configXMl);
- HashMap<String, String> customHeaders = new HashMap<String, String>();
- customHeaders.put("Content-Type", "application/xml;charset=UTF-8");
- Response serviceResponse = consumeService(headers, getServiceUri()
- + "/v2/jobs", HttpMethod.POST, configXMl, customHeaders);
-
- LOGGER
- .info("REsp from oozie status entity==" + serviceResponse.getEntity());
- if (serviceResponse.getEntity() instanceof String) {
- return (String) serviceResponse.getEntity();
- } else {
- return "success";
- }
-
- }
-
- private String createWorkflowFile(String postBody, String workflowFile, boolean overwrite) throws IOException, InterruptedException {
- FSDataOutputStream fsOut = getHdfsgetApi().create(workflowFile, overwrite);
- fsOut.write(postBody.getBytes());
- fsOut.close();
- return workflowFile;
- }
-
- private String buildURI(UriInfo ui) {
- String uiURI = ui.getAbsolutePath().getPath();
- int index = uiURI.indexOf("proxy/") + 5;
- uiURI = uiURI.substring(index);
- String serviceURI = getServiceUri();
- serviceURI += uiURI;
-
- MultivaluedMap<String, String> parameters = ui.getQueryParameters();
- StringBuilder urlBuilder = new StringBuilder(serviceURI);
- boolean firstEntry = true;
- for (Map.Entry<String, List<String>> entry : parameters.entrySet()) {
- if ("user.name".equals(entry.getKey())) {
- ArrayList<String> vals = new ArrayList<String>();
- vals.add(viewContext.getUsername());
- entry.setValue(vals);
- }
- if (firstEntry) {
- urlBuilder.append("?");
- } else {
- urlBuilder.append("&");
- }
- boolean firstVal = true;
- for (String val : entry.getValue()) {
- urlBuilder.append(firstVal ? "" : "&").append(entry.getKey())
- .append("=").append(val);
- firstVal = false;
- }
- firstEntry = false;
- }
- return urlBuilder.toString();
- }
-
- private String getServiceUri() {
- String serviceURI = viewContext.getProperties().get(SERVICE_URI_PROP) != null ? viewContext
- .getProperties().get(SERVICE_URI_PROP) : DEFAULT_SERVICE_URI;
- return serviceURI;
- }
-
- public Response consumeService(HttpHeaders headers, String urlToRead,
- String method, String body, Map<String, String> customHeaders) {
- Response response = null;
- InputStream stream = readFromOozie(headers, urlToRead, method, body,
- customHeaders);
- String stringResponse = null;
- try {
- stringResponse = IOUtils.toString(stream);
- } catch (IOException e) {
- LOGGER.error("Error while converting stream to string", e);
- throw new RuntimeException(e);
- }
- if (stringResponse.contains(Response.Status.BAD_REQUEST.name())) {
- response = Response.status(Response.Status.BAD_REQUEST)
- .entity(stringResponse).type(MediaType.TEXT_PLAIN).build();
- } else {
- response = Response.status(Response.Status.OK).entity(stringResponse)
- .type(deduceType(stringResponse)).build();
- }
- return response;
- }
-
- private InputStream readFromOozie(HttpHeaders headers, String urlToRead,
- String method, String body, Map<String, String> customHeaders) {
- URLStreamProvider streamProvider = viewContext.getURLStreamProvider();
- Map<String, String> newHeaders = getHeaders(headers);
- newHeaders.put(USER_NAME_HEADER, USER_OOZIE_SUPER);
-
- newHeaders.put(DO_AS_HEADER, viewContext.getUsername());
- newHeaders.put("Accept", MediaType.APPLICATION_JSON);
- if (customHeaders != null) {
- newHeaders.putAll(customHeaders);
- }
- LOGGER.info(String.format("Proxy request for url: [%s] %s", method,
- urlToRead));
- boolean securityEnabled = isSecurityEnabled();
- LOGGER.debug(String.format("IS security enabled:[%b]", securityEnabled));
- InputStream stream = null;
- try {
- if (securityEnabled) {
- stream = streamProvider.readAsCurrent(urlToRead, method, body, newHeaders);
-
- } else {
- stream = streamProvider.readFrom(urlToRead, method, body, newHeaders);
- }
- } catch (IOException e) {
- LOGGER.error("error talking to oozie", e);
- throw new RuntimeException(e);
- }
- return stream;
- }
-
- public Response consumeService(HttpHeaders headers, String urlToRead,
- String method, String body) throws Exception {
- return consumeService(headers, urlToRead, method, body, null);
- }
-
- public Map<String, String> getHeaders(HttpHeaders headers) {
- MultivaluedMap<String, String> requestHeaders = headers.getRequestHeaders();
- Set<Entry<String, List<String>>> headerEntrySet = requestHeaders.entrySet();
- HashMap<String, String> headersMap = new HashMap<String, String>();
- for (Entry<String, List<String>> headerEntry : headerEntrySet) {
- String key = headerEntry.getKey();
- List<String> values = headerEntry.getValue();
- headersMap.put(key, strJoin(values, ","));
- }
- return headersMap;
- }
-
- public String strJoin(List<String> strings, String separator) {
- StringBuilder stringBuilder = new StringBuilder();
- for (int i = 0, il = strings.size(); i < il; i++) {
- if (i > 0) {
- stringBuilder.append(separator);
- }
- stringBuilder.append(strings.get(i));
- }
- return stringBuilder.toString();
- }
-
- private MediaType deduceType(String stringResponse) {
- if (stringResponse.startsWith("{")) {
- return MediaType.APPLICATION_JSON_TYPE;
- } else if (stringResponse.startsWith("<")) {
- return MediaType.TEXT_XML_TYPE;
- } else {
- return MediaType.APPLICATION_JSON_TYPE;
- }
- }
-
- private HdfsApi getHdfsgetApi() {
- if (_hdfsApi == null) {
- try {
- _hdfsApi = HdfsUtil.connectToHDFSApi(viewContext);
- } catch (Exception ex) {
- LOGGER.error("Error in getting HDFS Api", ex);
- throw new RuntimeException("HdfsApi connection failed. Check \"webhdfs.url\" property", ex);
- }
- }
- return _hdfsApi;
- }
-
- private String generateConigXml(Map<String, String> map) {
- DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
- DocumentBuilder db;
- try {
- db = dbf.newDocumentBuilder();
- Document doc = db.newDocument();
- Element configElement = doc.createElement("configuration");
- doc.appendChild(configElement);
- for (Map.Entry<String, String> entry : map.entrySet()) {
- Element propElement = doc.createElement("property");
- configElement.appendChild(propElement);
- Element nameElem = doc.createElement("name");
- nameElem.setTextContent(entry.getKey());
- Element valueElem = doc.createElement("value");
- valueElem.setTextContent(entry.getValue());
- propElement.appendChild(nameElem);
- propElement.appendChild(valueElem);
- }
- DOMSource domSource = new DOMSource(doc);
- StringWriter writer = new StringWriter();
- StreamResult result = new StreamResult(writer);
- TransformerFactory tf = TransformerFactory.newInstance();
- Transformer transformer = tf.newTransformer();
- transformer.setOutputProperty(OutputKeys.INDENT, "yes");
- transformer
- .setOutputProperty(XML_INDENT_AMT_PROP_NAME, XML_INDENT_SPACES);
- transformer.transform(domSource, result);
- return writer.toString();
- } catch (ParserConfigurationException | TransformerException e) {
- LOGGER.error("error in generating config xml", e);
- throw new RuntimeException(e);
- }
-
- }
-
- private String formatXml(String xml) {
- DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
- try {
- DocumentBuilder db = dbf.newDocumentBuilder();
- StreamResult result = new StreamResult(new StringWriter());
- Document document = db.parse(new InputSource(new StringReader(xml)));
- Transformer transformer = TransformerFactory.newInstance()
- .newTransformer();
- transformer.setOutputProperty(OutputKeys.INDENT, "yes");
- transformer
- .setOutputProperty(XML_INDENT_AMT_PROP_NAME, XML_INDENT_SPACES);
- DOMSource source = new DOMSource(document);
- transformer.transform(source, result);
- return result.getWriter().toString();
- } catch (ParserConfigurationException | SAXException | IOException
- | TransformerFactoryConfigurationError | TransformerException e) {
- LOGGER.error("Error in formatting xml", e);
- throw new RuntimeException(e);
- }
- }
-
- private boolean isSecurityEnabled() {
- boolean securityEnabled = Boolean.valueOf(getHadoopConfigs().get(
- "security_enabled"));
- return securityEnabled;
- }
-
- private Map<String, String> getHadoopConfigs() {
- return viewContext.getInstanceData();
- }
-
-}
+ private final static Logger LOGGER = LoggerFactory
+ .getLogger(OozieProxyImpersonator.class);
+
+ private static final String OOZIEPARAM_PREFIX = "oozieparam.";
+ private static final int OOZIEPARAM_PREFIX_LENGTH = OOZIEPARAM_PREFIX
+ .length();
+ private static final String EQUAL_SYMBOL = "=";
+ private static final String OOZIE_WF_RERUN_FAILNODES_CONF_KEY = "oozie.wf.rerun.failnodes";
+ private static final String OOZIE_USE_SYSTEM_LIBPATH_CONF_KEY = "oozie.use.system.libpath";
+
+ private ViewContext viewContext;
+
+ private static final String USER_NAME_HEADER = "user.name";
+ private static final String USER_OOZIE_SUPER = "oozie";
+ private static final String DO_AS_HEADER = "doAs";
+
+ private static final String SERVICE_URI_PROP = "oozie.service.uri";
+ private static final String DEFAULT_SERVICE_URI = "http://sandbox.hortonworks.com:11000/oozie";
+ private Utils utils=new Utils();
+ private OozieUtils oozieUtils=new OozieUtils();
+ private HDFSFileUtils hdfsFileUtils;
+ private static enum ErrorCodes {
+ OOZIE_SUBMIT_ERROR("error.oozie.submit", "Oozie Submit error"), OOZIE_IO_ERROR(
+ "error.oozie.io", "Oozie I/O error"), FILE_ACCESS_ACL_ERROR(
+ "error.file.access.control",
+ "Access Error to file due to access control"), FILE_ACCESS_UNKNOWN_ERROR(
+ "error.file.access", "Error accessing file"), WORKFLOW_PATH_EXISTS(
+ "error.workflow.path.exists", "Worfklow path exists");
+ private String errorCode;
+ private String description;
+
+ ErrorCodes(String errorCode, String description) {
+ this.errorCode = errorCode;
+ this.description = description;
+ }
+
+ public String getErrorCode() {
+ return errorCode;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+ }
+
+ @Inject
+ public OozieProxyImpersonator(ViewContext viewContext) {
+ this.viewContext = viewContext;
+ hdfsFileUtils=new HDFSFileUtils(viewContext);
+ LOGGER.info(String.format(
+ "OozieProxyImpersonator initialized for instance: %s",
+ viewContext.getInstanceName()));
+ }
+
+ @Path("/fileServices")
+ public FileServices fileServices() {
+ return new FileServices(viewContext);
+ }
+
+ @GET
+ @Path("/getCurrentUserName")
+ public Response getCurrentUserName() {
+ return Response.ok(viewContext.getUsername()).build();
+ }
+
+ @POST
+ @Path("/submitJob")
+ @Consumes({ MediaType.TEXT_PLAIN + "," + MediaType.TEXT_XML })
+ public Response submitJob(String postBody, @Context HttpHeaders headers,
+ @Context UriInfo ui, @QueryParam("app.path") String appPath,
+ @DefaultValue("false") @QueryParam("overwrite") Boolean overwrite,
+ @QueryParam("jobType") String jobType) {
+ LOGGER.info("submit workflow job called");
+ return submitJobInternal(postBody, headers, ui, appPath, overwrite,
+ JobType.valueOf(jobType));
+ }
+
+ @POST
+ @Path("/submitWorkflow")
+ @Consumes({ MediaType.TEXT_PLAIN + "," + MediaType.TEXT_XML })
+ public Response submitWorkflow(String postBody,
+ @Context HttpHeaders headers, @Context UriInfo ui,
+ @QueryParam("app.path") String appPath,
+ @DefaultValue("false") @QueryParam("overwrite") Boolean overwrite) {
+ LOGGER.info("submit workflow job called");
+ return submitJobInternal(postBody, headers, ui, appPath, overwrite,
+ JobType.WORKFLOW);
+ }
+
+ @POST
+ @Path("/saveWorkflow")
+ @Consumes({ MediaType.TEXT_PLAIN + "," + MediaType.TEXT_XML })
+ public Response saveWorkflow(String postBody, @Context HttpHeaders headers,
+ @Context UriInfo ui, @QueryParam("app.path") String appPath,
+ @DefaultValue("false") @QueryParam("overwrite") Boolean overwrite) {
+ LOGGER.info("save workflow called");
+ if (StringUtils.isEmpty(appPath)) {
+ throw new RuntimeException("app path can't be empty.");
+ }
+ appPath = appPath.trim();
+ if (!overwrite) {
+ boolean fileExists = hdfsFileUtils.fileExists(appPath);
+ if (fileExists) {
+ return getFileExistsResponse();
+ }
+ }
+ postBody = utils.formatXml(postBody);
+ try {
+ String filePath = hdfsFileUtils.createWorkflowFile(getWorkflowFileName(appPath),postBody, overwrite);
+ LOGGER.info(String.format(
+ "submit workflow job done. filePath=[%s]", filePath));
+ return Response.ok().build();
+ } catch (Exception ex) {
+ LOGGER.error(ex.getMessage(), ex);
+ return getRespCodeForException(ex);
+
+ }
+ }
+
+ private Response submitJobInternal(String postBody, HttpHeaders headers,
+ UriInfo ui, String appPath, Boolean overwrite, JobType jobType) {
+ if (StringUtils.isEmpty(appPath)) {
+ throw new RuntimeException("app path can't be empty.");
+ }
+ appPath = appPath.trim();
+ if (!overwrite) {
+ boolean fileExists = hdfsFileUtils.fileExists(appPath);
+ if (fileExists) {
+ return getFileExistsResponse();
+ }
+ }
+ postBody = utils.formatXml(postBody);
+ try {
+ String filePath = hdfsFileUtils.createWorkflowFile(getWorkflowFileName(appPath),postBody,overwrite);
+ LOGGER.info(String.format(
+ "submit workflow job done. filePath=[%s]", filePath));
+ } catch (Exception ex) {
+ LOGGER.error(ex.getMessage(), ex);
+ return getRespCodeForException(ex);
+
+ }
+ String response = submitWorkflowJobToOozie(headers, appPath,
+ ui.getQueryParameters(), jobType);
+ if (response != null && response.trim().startsWith("{")) {
+ // dealing with oozie giving error but with 200 response.
+ return Response.status(Response.Status.OK).entity(response).build();
+ } else {
+ HashMap<String, String> resp = new HashMap<String, String>();
+ resp.put("status", ErrorCodes.OOZIE_SUBMIT_ERROR.getErrorCode());
+ resp.put("message", response);
+ return Response.status(Response.Status.BAD_REQUEST).entity(resp)
+ .build();
+ }
+
+ }
+
+ private Response getRespCodeForException(Exception ex) {
+ if (ex instanceof AccessControlException) {
+ HashMap<String, String> errorDetails = getErrorDetails(
+ ErrorCodes.FILE_ACCESS_ACL_ERROR.getErrorCode(),
+ ErrorCodes.FILE_ACCESS_ACL_ERROR.getDescription(), ex);
+ return Response.status(Response.Status.BAD_REQUEST)
+ .entity(errorDetails).build();
+ }else if (ex instanceof IOException){
+ HashMap<String, String> errorDetails = getErrorDetails(
+ ErrorCodes.FILE_ACCESS_UNKNOWN_ERROR.getErrorCode(),
+ ErrorCodes.FILE_ACCESS_UNKNOWN_ERROR.getDescription(), ex);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
+ .entity(errorDetails).build();
+ }else {
+ HashMap<String, String> errorDetails = getErrorDetails(
+ ErrorCodes.FILE_ACCESS_UNKNOWN_ERROR.getErrorCode(),
+ ErrorCodes.FILE_ACCESS_UNKNOWN_ERROR.getDescription(), ex);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
+ .entity(errorDetails).build();
+ }
+
+
+ }
+
+ private Response getFileExistsResponse() {
+ HashMap<String, String> resp = new HashMap<String, String>();
+ resp.put("status", ErrorCodes.WORKFLOW_PATH_EXISTS.getErrorCode());
+ resp.put("message", ErrorCodes.WORKFLOW_PATH_EXISTS.getDescription());
+ return Response.status(Response.Status.BAD_REQUEST).entity(resp)
+ .build();
+ }
+
+ private String getWorkflowFileName(String appPath) {
+ String workflowFile = null;
+ if (appPath.endsWith(".xml")) {
+ workflowFile = appPath;
+ } else {
+ workflowFile = appPath + (appPath.endsWith("/") ? "" : "/")
+ + "workflow.xml";
+ }
+ return workflowFile;
+ }
+
+ @GET
+ @Path("/readWorkflowXml")
+ public Response readWorkflowXxml(
+ @QueryParam("workflowXmlPath") String workflowPath) {
+ if (StringUtils.isEmpty(workflowPath)) {
+ throw new RuntimeException("workflowXmlPath can't be empty.");
+ }
+ try {
+ final FSDataInputStream is = hdfsFileUtils.read(workflowPath);
+ StreamingOutput streamer = new StreamingOutput() {
+ @Override
+ public void write(OutputStream os) throws IOException,
+ WebApplicationException {
+ IOUtils.copy(is, os);
+ is.close();
+ os.close();
+ }
+ };
+ return Response.ok(streamer).status(200).build();
+ } catch(IOException e){
+ return getRespCodeForException(e);
+ }
+ }
+
+ private HashMap<String, String> getErrorDetails(String status,
+ String message, Exception ex) {
+ HashMap<String, String> resp = new HashMap<String, String>();
+ resp.put("status", status);
+ if (message != null) {
+ resp.put("message", message);
+ }
+ if (ex != null) {
+ resp.put("stackTrace", ExceptionUtils.getFullStackTrace(ex));
+ }
+ return resp;
+ }
+
+ @GET
+ @Path("/getDag")
+ @Produces("image/png")
+ public Response submitWorkflow(@Context HttpHeaders headers,
+ @Context UriInfo ui, @QueryParam("jobid") String jobid) {
+ String imgUrl = getServiceUri() + "/v2/job/" + jobid + "?show=graph";
+ Map<String, String> newHeaders = utils.getHeaders(headers);
+ final InputStream is = readFromOozie(headers, imgUrl, HttpMethod.GET,
+ null, newHeaders);
+ StreamingOutput streamer = new StreamingOutput() {
+
+ @Override
+ public void write(OutputStream os) throws IOException,
+ WebApplicationException {
+ IOUtils.copy(is, os);
+ is.close();
+ os.close();
+ }
+
+ };
+ return Response.ok(streamer).status(200).build();
+ }
+
+ @GET
+ @Path("/{path: .*}")
+ public Response handleGet(@Context HttpHeaders headers, @Context UriInfo ui) {
+ try {
+ String serviceURI = buildURI(ui);
+ return consumeService(headers, serviceURI, HttpMethod.GET, null);
+ } catch (Exception ex) {
+ LOGGER.error("Error in GET proxy", ex);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
+ .entity(getErrorDetailsForException("Oozie", ex)).build();
+ }
+ }
+
+ @POST
+ @Path("/{path: .*}")
+ public Response handlePost(String xml, @Context HttpHeaders headers,
+ @Context UriInfo ui) {
+ try {
+ String serviceURI = buildURI(ui);
+ return consumeService(headers, serviceURI, HttpMethod.POST, xml);
+ } catch (Exception ex) {
+ LOGGER.error("Error in POST proxy", ex);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
+ .entity(getErrorDetailsForException("Oozie", ex)).build();
+ }
+ }
+
+ @DELETE
+ @Path("/{path: .*}")
+ public Response handleDelete(@Context HttpHeaders headers,
+ @Context UriInfo ui) {
+ try {
+ String serviceURI = buildURI(ui);
+ return consumeService(headers, serviceURI, HttpMethod.POST, null);
+ } catch (Exception ex) {
+ LOGGER.error("Error in DELETE proxy", ex);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
+ .entity(getErrorDetailsForException("Oozie", ex)).build();
+ }
+ }
+
+ @PUT
+ @Path("/{path: .*}")
+ public Response handlePut(String body, @Context HttpHeaders headers,
+ @Context UriInfo ui) {
+ try {
+ String serviceURI = buildURI(ui);
+ return consumeService(headers, serviceURI, HttpMethod.PUT, body);
+ } catch (Exception ex) {
+ LOGGER.error("Error in PUT proxy", ex);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
+ .entity(getErrorDetailsForException("Oozie", ex)).build();
+ }
+ }
+
+ private Map<String, String> getErrorDetailsForException(String component,
+ Exception ex) {
+ String errorCode = component + "exception";
+ String errorMessage = component + " Exception";
+ if (ex instanceof RuntimeException) {
+ Throwable cause = ex.getCause();
+ if (cause instanceof IOException) {
+ errorCode = component + "io.exception";
+ errorMessage = component + "IO Exception";
+ }
+ }
+ return getErrorDetails(errorCode, errorMessage, ex);
+ }
+
+ private String submitWorkflowJobToOozie(HttpHeaders headers,
+ String filePath, MultivaluedMap<String, String> queryParams,
+ JobType jobType) {
+ String nameNode = "hdfs://"
+ + viewContext.getCluster().getConfigurationValue("hdfs-site",
+ "dfs.namenode.rpc-address");
+
+ if (!queryParams.containsKey("config.nameNode")) {
+ ArrayList<String> nameNodes = new ArrayList<String>();
+ LOGGER.info("Namenode===" + nameNode);
+ nameNodes.add(nameNode);
+ queryParams.put("config.nameNode", nameNodes);
+ }
+
+ HashMap<String, String> workflowConigs = getWorkflowConfigs(filePath,
+ queryParams, jobType, nameNode);
+ String configXMl = oozieUtils.generateConfigXml(workflowConigs);
+ LOGGER.info("Config xml==" + configXMl);
+ HashMap<String, String> customHeaders = new HashMap<String, String>();
+ customHeaders.put("Content-Type", "application/xml;charset=UTF-8");
+ Response serviceResponse = consumeService(headers, getServiceUri()
+ + "/v2/jobs?" + getJobSumbitOozieParams(queryParams),
+ HttpMethod.POST, configXMl, customHeaders);
+
+ LOGGER.info("REsp from oozie status entity=="
+ + serviceResponse.getEntity());
+ if (serviceResponse.getEntity() instanceof String) {
+ return (String) serviceResponse.getEntity();
+ } else {
+ return "success";
+ }
+
+ }
+
+ private HashMap<String, String> getWorkflowConfigs(String filePath,
+ MultivaluedMap<String, String> queryParams, JobType jobType,
+ String nameNode) {
+ HashMap<String, String> workflowConigs = new HashMap<String, String>();
+ if (queryParams.containsKey("resourceManager")
+ && "useDefault".equals(queryParams.getFirst("resourceManager"))) {
+ String jobTrackerNode = viewContext.getCluster()
+ .getConfigurationValue("yarn-site",
+ "yarn.resourcemanager.address");
+ LOGGER.info("jobTrackerNode===" + jobTrackerNode);
+ workflowConigs.put("resourceManager", jobTrackerNode);
+ workflowConigs.put("jobTracker", jobTrackerNode);
+ }
+ if (queryParams != null) {
+ for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {
+ if (entry.getKey().startsWith("config.")) {
+ if (entry.getValue() != null && entry.getValue().size() > 0) {
+ workflowConigs.put(entry.getKey().substring(7), entry
+ .getValue().get(0));
+ }
+ }
+ }
+ }
+
+ if (queryParams.containsKey("oozieconfig.useSystemLibPath")) {
+ String useSystemLibPath = queryParams
+ .getFirst("oozieconfig.useSystemLibPath");
+ workflowConigs.put(OOZIE_USE_SYSTEM_LIBPATH_CONF_KEY,
+ useSystemLibPath);
+ } else {
+ workflowConigs.put(OOZIE_USE_SYSTEM_LIBPATH_CONF_KEY, "true");
+ }
+ if (queryParams.containsKey("oozieconfig.rerunOnFailure")) {
+ String rerunFailnodes = queryParams
+ .getFirst("oozieconfig.rerunOnFailure");
+ workflowConigs.put(OOZIE_WF_RERUN_FAILNODES_CONF_KEY,
+ rerunFailnodes);
+ } else {
+ workflowConigs.put(OOZIE_WF_RERUN_FAILNODES_CONF_KEY, "true");
+ }
+ workflowConigs.put("user.name", viewContext.getUsername());
+ workflowConigs.put(oozieUtils.getJobPathPropertyKey(jobType), nameNode + filePath);
+ return workflowConigs;
+ }
+
+ private String getJobSumbitOozieParams(
+ MultivaluedMap<String, String> queryParams) {
+ StringBuilder query = new StringBuilder();
+ if (queryParams != null) {
+ for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {
+ if (entry.getKey().startsWith(OOZIEPARAM_PREFIX)) {
+ if (entry.getValue() != null && entry.getValue().size() > 0) {
+ for (String val : entry.getValue()) {
+ query.append(
+ entry.getKey().substring(
+ OOZIEPARAM_PREFIX_LENGTH))
+ .append(EQUAL_SYMBOL).append(val)
+ .append("&");
+ }
+ }
+ }
+ }
+ }
+ return query.toString();
+ }
+
+ private String buildURI(UriInfo ui) {
+ String uiURI = ui.getAbsolutePath().getPath();
+ int index = uiURI.indexOf("proxy/") + 5;
+ uiURI = uiURI.substring(index);
+ String serviceURI = getServiceUri();
+ serviceURI += uiURI;
+ MultivaluedMap<String, String> params = addOrReplaceUserName(ui.getQueryParameters());
+ return serviceURI+utils.convertParamsToUrl(params);
+ }
+ private MultivaluedMap<String, String> addOrReplaceUserName(MultivaluedMap<String, String> parameters){
+ for (Map.Entry<String, List<String>> entry : parameters.entrySet()) {
+ if ("user.name".equals(entry.getKey())) {
+ ArrayList<String> vals = new ArrayList<String>(1);
+ vals.add(viewContext.getUsername());
+ entry.setValue(vals);
+ }
+ }
+ return parameters;
+ }
+
+ private String getServiceUri() {
+ String serviceURI = viewContext.getProperties().get(SERVICE_URI_PROP) != null ? viewContext
+ .getProperties().get(SERVICE_URI_PROP) : DEFAULT_SERVICE_URI;
+ return serviceURI;
+ }
+
+ private Response consumeService(HttpHeaders headers, String urlToRead,
+ String method, String body) throws Exception {
+ return consumeService(headers, urlToRead, method, body, null);
+ }
+
+ private Response consumeService(HttpHeaders headers, String urlToRead,
+ String method, String body, Map<String, String> customHeaders) {
+ Response response = null;
+ InputStream stream = readFromOozie(headers, urlToRead, method, body,
+ customHeaders);
+ String stringResponse = null;
+ try {
+ stringResponse = IOUtils.toString(stream);
+ } catch (IOException e) {
+ LOGGER.error("Error while converting stream to string", e);
+ throw new RuntimeException(e);
+ }
+ if (stringResponse.contains(Response.Status.BAD_REQUEST.name())) {
+ response = Response.status(Response.Status.BAD_REQUEST)
+ .entity(stringResponse).type(MediaType.TEXT_PLAIN).build();
+ } else {
+ response = Response.status(Response.Status.OK)
+ .entity(stringResponse).type(utils.deduceType(stringResponse))
+ .build();
+ }
+ return response;
+ }
+
+ private InputStream readFromOozie(HttpHeaders headers, String urlToRead,
+ String method, String body, Map<String, String> customHeaders) {
+
+ Map<String, String> newHeaders = utils.getHeaders(headers);
+ newHeaders.put(USER_NAME_HEADER, USER_OOZIE_SUPER);
+
+ newHeaders.put(DO_AS_HEADER, viewContext.getUsername());
+ newHeaders.put("Accept", MediaType.APPLICATION_JSON);
+ if (customHeaders != null) {
+ newHeaders.putAll(customHeaders);
+ }
+ LOGGER.info(String.format("Proxy request for url: [%s] %s", method,
+ urlToRead));
+
+ return readFromUrl(urlToRead, method, body, newHeaders);
+ }
+
+ private InputStream readFromUrl(String urlToRead, String method, String body,
+ Map<String, String> newHeaders) {
+ URLStreamProvider streamProvider = viewContext.getURLStreamProvider();
+ InputStream stream = null;
+ try {
+ if (isSecurityEnabled()) {
+ stream = streamProvider.readAsCurrent(urlToRead, method, body,
+ newHeaders);
+
+ } else {
+ stream = streamProvider.readFrom(urlToRead, method, body,
+ newHeaders);
+ }
+ } catch (IOException e) {
+ LOGGER.error("error talking to oozie", e);
+ throw new RuntimeException(e);
+ }
+ return stream;
+ }
+
+
+ private boolean isSecurityEnabled() {
+ String authType = viewContext.getCluster().getConfigurationValue(
+ "core-site", "hadoop.security.authentication");
+ LOGGER.info("Auth Type=" + authType);
+ return !"simple".equalsIgnoreCase(authType);
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/7c7412ed/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/OozieUtils.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/OozieUtils.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/OozieUtils.java
new file mode 100644
index 0000000..170132f
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/OozieUtils.java
@@ -0,0 +1,71 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.ambari.view;
+
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class OozieUtils {
+ private final static Logger LOGGER = LoggerFactory
+ .getLogger(OozieUtils.class);
+ private Utils utils = new Utils();
+
+ public String generateConfigXml(Map<String, String> map) {
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ DocumentBuilder db;
+ try {
+ db = dbf.newDocumentBuilder();
+ Document doc = db.newDocument();
+ Element configElement = doc.createElement("configuration");
+ doc.appendChild(configElement);
+ for (Map.Entry<String, String> entry : map.entrySet()) {
+ Element propElement = doc.createElement("property");
+ configElement.appendChild(propElement);
+ Element nameElem = doc.createElement("name");
+ nameElem.setTextContent(entry.getKey());
+ Element valueElem = doc.createElement("value");
+ valueElem.setTextContent(entry.getValue());
+ propElement.appendChild(nameElem);
+ propElement.appendChild(valueElem);
+ }
+ return utils.generateXml(doc);
+ } catch (ParserConfigurationException e) {
+ LOGGER.error("error in generating config xml", e);
+ throw new RuntimeException(e);
+ }
+ }
+ public String getJobPathPropertyKey(JobType jobType) {
+ switch (jobType) {
+ case WORKFLOW:
+ return "oozie.wf.application.path";
+ case COORDINATOR:
+ return "oozie.coord.application.path";
+ case BUNDLE:
+ return "oozie.bundle.application.path";
+ }
+ throw new RuntimeException("Unknown Job Type");
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/7c7412ed/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/Utils.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/Utils.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/Utils.java
new file mode 100644
index 0000000..61d878e
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/Utils.java
@@ -0,0 +1,154 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.ambari.view;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+public class Utils {
+ private static final String XML_INDENT_SPACES = "4";
+ private static final String XML_INDENT_AMT_PROP_NAME = "{http://xml.apache.org/xslt}indent-amount";
+ private final static Logger LOGGER = LoggerFactory
+ .getLogger(Utils.class);
+ public String formatXml(String xml) {
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ try {
+ DocumentBuilder db = dbf.newDocumentBuilder();
+ StreamResult result = new StreamResult(new StringWriter());
+ Document document = db
+ .parse(new InputSource(new StringReader(xml)));
+ Transformer transformer = TransformerFactory.newInstance()
+ .newTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty(XML_INDENT_AMT_PROP_NAME,
+ XML_INDENT_SPACES);
+ DOMSource source = new DOMSource(document);
+ transformer.transform(source, result);
+ return result.getWriter().toString();
+ } catch (ParserConfigurationException | SAXException | IOException
+ | TransformerFactoryConfigurationError | TransformerException e) {
+ LOGGER.error("Error in formatting xml", e);
+ throw new RuntimeException(e);
+ }
+ }
+ public String generateXml(Document doc){
+ DOMSource domSource = new DOMSource(doc);
+ StringWriter writer = new StringWriter();
+ StreamResult result = new StreamResult(writer);
+ TransformerFactory tf = TransformerFactory.newInstance();
+ Transformer transformer;
+ try {
+ transformer = tf.newTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty(XML_INDENT_AMT_PROP_NAME,
+ XML_INDENT_SPACES);
+ try {
+ transformer.transform(domSource, result);
+ } catch (TransformerException e) {
+ throw new RuntimeException(e);
+ }
+ return writer.toString();
+ } catch (TransformerConfigurationException tce) {
+ throw new RuntimeException(tce);
+ }
+
+ }
+
+ public Map<String, String> getHeaders(HttpHeaders headers) {
+ MultivaluedMap<String, String> requestHeaders = headers
+ .getRequestHeaders();
+ Set<Entry<String, List<String>>> headerEntrySet = requestHeaders
+ .entrySet();
+ HashMap<String, String> headersMap = new HashMap<String, String>();
+ for (Entry<String, List<String>> headerEntry : headerEntrySet) {
+ String key = headerEntry.getKey();
+ List<String> values = headerEntry.getValue();
+ headersMap.put(key, strJoin(values, ","));
+ }
+ return headersMap;
+ }
+
+ public String strJoin(List<String> strings, String separator) {
+ StringBuilder stringBuilder = new StringBuilder();
+ for (int i = 0, il = strings.size(); i < il; i++) {
+ if (i > 0) {
+ stringBuilder.append(separator);
+ }
+ stringBuilder.append(strings.get(i));
+ }
+ return stringBuilder.toString();
+ }
+ public MediaType deduceType(String stringResponse) {
+ if (stringResponse.startsWith("{")) {
+ return MediaType.APPLICATION_JSON_TYPE;
+ } else if (stringResponse.startsWith("<")) {
+ return MediaType.TEXT_XML_TYPE;
+ } else {
+ return MediaType.APPLICATION_JSON_TYPE;
+ }
+ }
+
+ public String convertParamsToUrl(MultivaluedMap<String, String> parameters) {
+ StringBuilder urlBuilder = new StringBuilder();
+ boolean firstEntry = true;
+ for (Map.Entry<String, List<String>> entry : parameters.entrySet()) {
+ if (firstEntry) {
+ urlBuilder.append("?");
+ } else {
+ urlBuilder.append("&");
+ }
+ boolean firstVal = true;
+ for (String val : entry.getValue()) {
+ urlBuilder.append(firstVal ? "" : "&").append(entry.getKey())
+ .append("=").append(val);
+ firstVal = false;
+ }
+ firstEntry = false;
+ }
+ return urlBuilder.toString();
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/7c7412ed/contrib/views/wfmanager/src/main/resources/ui/.jshintrc
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/.jshintrc b/contrib/views/wfmanager/src/main/resources/ui/.jshintrc
new file mode 100644
index 0000000..ed9a144
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/.jshintrc
@@ -0,0 +1,38 @@
+{
+ "predef": [
+ "document",
+ "window",
+ "-Promise",
+ "vkbeautify",
+ "moment",
+ "X2JS",
+ "jsPlumb",
+ "cytoscape",
+ "dagre"
+ ],
+ "browser": true,
+ "boss": true,
+ "curly": true,
+ "debug": false,
+ "devel": true,
+ "eqeqeq": true,
+ "evil": true,
+ "forin": false,
+ "immed": false,
+ "laxbreak": false,
+ "newcap": true,
+ "noarg": true,
+ "noempty": false,
+ "nonew": false,
+ "nomen": false,
+ "onevar": false,
+ "plusplus": false,
+ "regexp": false,
+ "undef": true,
+ "sub": true,
+ "strict": false,
+ "white": false,
+ "eqnull": true,
+ "esnext": true,
+ "unused": true
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/7c7412ed/contrib/views/wfmanager/src/main/resources/ui/app/components/.gitkeep
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/.gitkeep b/contrib/views/wfmanager/src/main/resources/ui/app/components/.gitkeep
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/ambari/blob/7c7412ed/contrib/views/wfmanager/src/main/resources/ui/app/components/archive-config.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/archive-config.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/archive-config.js
index d53b459..7ea46c4 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/components/archive-config.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/archive-config.js
@@ -16,9 +16,8 @@
*/
import Ember from 'ember';
-import EmberValidations from 'ember-validations';
-export default Ember.Component.extend(EmberValidations,{
+export default Ember.Component.extend({
fileBrowser : Ember.inject.service('file-browser'),
initialize : function(){
this.on('fileSelected',function(fileName){
http://git-wip-us.apache.org/repos/asf/ambari/blob/7c7412ed/contrib/views/wfmanager/src/main/resources/ui/app/components/bundle-config.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/bundle-config.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/bundle-config.js
new file mode 100644
index 0000000..2799db5
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/bundle-config.js
@@ -0,0 +1,262 @@
+/*
+* 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.
+*/
+import Ember from 'ember';
+import {Bundle} from '../domain/bundle/bundle';
+import {BundleGenerator} from '../domain/bundle/bundle-xml-generator';
+import {BundleXmlImporter} from '../domain/bundle/bundle-xml-importer';
+import { validator, buildValidations } from 'ember-cp-validations';
+import Constants from '../utils/constants';
+
+const Validations = buildValidations({
+ 'bundle.name': validator('presence', {
+ presence : true
+ }),
+ 'bundle.coordinators': {
+ validators: [
+ validator('operand-length', {
+ min : 1,
+ dependentKeys: ['bundle','bundle.coordinators.[]'],
+ message : 'Alteast one coordinator is required',
+ disabled(model, attribute) {
+ return !model.get('bundle');
+ }
+ })
+ ]
+ }
+});
+
+export default Ember.Component.extend(Ember.Evented, Validations, {
+ bundle : null,
+ propertyExtractor : Ember.inject.service('property-extractor'),
+ fileBrowser : Ember.inject.service('file-browser'),
+ workspaceManager : Ember.inject.service('workspace-manager'),
+ initialize : function(){
+ var draftBundle = this.get('workspaceManager').restoreWorkInProgress(this.get('tabInfo.id'));
+ if(draftBundle){
+ this.set('bundle', JSON.parse(draftBundle));
+ }else{
+ this.set('bundle', this.createBundle());
+ }
+ this.get('fileBrowser').on('fileBrowserOpened',function(context){
+ this.get('fileBrowser').setContext(context);
+ }.bind(this));
+ this.on('fileSelected',function(fileName){
+ this.set(this.get('filePathModel'), fileName);
+ }.bind(this));
+ if(Ember.isBlank(this.get('bundle.name'))){
+ this.set('bundle.name', Ember.copy(this.get('tabInfo.name')));
+ }
+ this.set('showErrorMessage', false);
+ this.schedulePersistWorkInProgress();
+ }.on('init'),
+ onDestroy : function(){
+ Ember.run.cancel(this.schedulePersistWorkInProgress);
+ this.persistWorkInProgress();
+ }.on('willDestroyElement'),
+ observeFilePath : Ember.observer('bundleFilePath', function(){
+ if(!this.get('bundleFilePath') || null === this.get('bundleFilePath')){
+ return;
+ }else{
+ this.sendAction('changeFilePath', this.get('tabInfo'), this.get('bundleFilePath'));
+ }
+ }),
+ nameObserver : Ember.observer('bundle.name', function(){
+ if(!this.get('bundle')){
+ return;
+ }else if(this.get('bundle') && Ember.isBlank(this.get('bundle.name'))){
+ if(!this.get('clonedTabInfo')){
+ this.set('clonedTabInfo', Ember.copy(this.get('tabInfo')));
+ }
+ this.sendAction('changeTabName', this.get('tabInfo'), this.get('clonedTabInfo.name'));
+ }else{
+ this.sendAction('changeTabName', this.get('tabInfo'), this.get('bundle.name'));
+ }
+ }),
+ schedulePersistWorkInProgress (){
+ Ember.run.later(function(){
+ this.persistWorkInProgress();
+ this.schedulePersistWorkInProgress();
+ }.bind(this), Constants.persistWorkInProgressInterval);
+ },
+ persistWorkInProgress (){
+ if(!this.get('bundle')){
+ return;
+ }
+ var json = JSON.stringify(this.get("bundle"));
+ this.get('workspaceManager').saveWorkInProgress(this.get('tabInfo.id'), json);
+ },
+ createBundle (){
+ return Bundle.create({
+ name : '',
+ kickOffTime : {
+ value : '',
+ displayValue : '',
+ type : 'date'
+ },
+ coordinators : null
+ });
+ },
+ importSampleBundle (){
+ var deferred = Ember.RSVP.defer();
+ Ember.$.ajax({
+ url: "/sampledata/bundle.xml",
+ dataType: "text",
+ cache:false,
+ success: function(data) {
+ deferred.resolve(data);
+ }.bind(this),
+ failure : function(data){
+ deferred.reject(data);
+ }
+ });
+ return deferred;
+ },
+ importBundle (filePath){
+ this.set("bundleFilePath", filePath);
+ this.set("isImporting", false);
+ var deferred = this.getBundleFromHdfs(filePath);
+ deferred.promise.then(function(data){
+ this.getBundleFromXml(data);
+ this.set("isImporting", false);
+ }.bind(this)).catch(function(){
+ this.set("isImporting", false);
+ this.set("isImportingSuccess", false);
+ }.bind(this));
+ },
+ getBundleFromHdfs(filePath){
+ var url = Ember.ENV.API_URL + "/readWorkflowXml?workflowXmlPath="+filePath;
+ var deferred = Ember.RSVP.defer();
+ Ember.$.ajax({
+ url: url,
+ method: 'GET',
+ dataType: "text",
+ beforeSend: function (xhr) {
+ xhr.setRequestHeader("X-XSRF-HEADER", Math.round(Math.random()*100000));
+ xhr.setRequestHeader("X-Requested-By", "Ambari");
+ }
+ }).done(function(data){
+ deferred.resolve(data);
+ }).fail(function(){
+ deferred.reject();
+ });
+ return deferred;
+ },
+ getBundleFromXml(bundleXml){
+ var bundleXmlImporter = BundleXmlImporter.create({});
+ var bundle = bundleXmlImporter.importBundle(bundleXml);
+ this.set("bundle", bundle);
+ },
+ actions : {
+ closeFileBrowser(){
+ this.set("showingFileBrowser", false);
+ this.get('fileBrowser').getContext().trigger('fileSelected', this.get('filePath'));
+ if(this.get('bundleFilePath')){
+ this.importBundle(Ember.copy(this.get('bundleFilePath')));
+ this.set('bundleFilePath', null);
+ }
+ },
+ openFileBrowser(model, context){
+ if(!context){
+ context = this;
+ }
+ this.get('fileBrowser').trigger('fileBrowserOpened',context);
+ this.set('filePathModel', model);
+ this.set('showingFileBrowser', true);
+ },
+ createCoordinator(){
+ this.set('coordinatorEditMode', false);
+ this.set('coordinatorCreateMode', true);
+ this.set('currentCoordinator',{
+ name : undefined,
+ appPath : undefined,
+ configuration : {
+ property : Ember.A([])
+ }
+ });
+ },
+ editCoordinator(index){
+ this.set('coordinatorEditMode', true);
+ this.set('coordinatorCreateMode', false);
+ this.set('currentCoordinatorIndex', index);
+ this.set('currentCoordinator', Ember.copy(this.get('bundle.coordinators').objectAt(index)));
+ },
+ addCoordinator(){
+ if(!this.get('bundle.coordinators')){
+ this.set('bundle.coordinators', Ember.A([]));
+ }
+ this.get('bundle.coordinators').pushObject(Ember.copy(this.get('currentCoordinator')));
+ this.set('coordinatorCreateMode', false);
+ },
+ updateCoordinator(){
+ this.get('bundle.coordinators').replace(this.get('currentCoordinatorIndex'), 1, Ember.copy(this.get('currentCoordinator')));
+ this.set('coordinatorEditMode', false);
+ },
+ deleteCoordinator(index){
+ this.get('bundle.coordinators').removeAt(index);
+ if(index === this.get('currentCoordinatorIndex')){
+ this.set('coordinatorEditMode', false);
+ }
+ },
+ cancelCoordinatorOperation(){
+ this.set('coordinatorCreateMode', false);
+ this.set('coordinatorEditMode', false);
+ },
+ confirmReset(){
+ this.set('showingResetConfirmation', true);
+ },
+ resetBundle(){
+ this.set('bundle', this.createBundle());
+ },
+ closeBundleSubmitConfig(){
+ this.set("showingJobConfig", false);
+ },
+ submitBundle(){
+ if(this.get('validations.isInvalid')) {
+ this.set('showErrorMessage', true);
+ return;
+ }
+ var bundleGenerator = BundleGenerator.create({bundle:this.get("bundle")});
+ var bundleXml = bundleGenerator.process();
+ var dynamicProperties = this.get('propertyExtractor').getDynamicProperties(bundleXml);
+ var configForSubmit = {props : dynamicProperties, xml : bundleXml, params : this.get('bundle.parameters')};
+ this.set("bundleConfigs", configForSubmit);
+ this.set("showingJobConfig", true);
+ },
+ preview(){
+ if(this.get('validations.isInvalid')) {
+ this.set('showErrorMessage', true);
+ return;
+ }
+ this.set("showingPreview", false);
+ var bundleGenerator = BundleGenerator.create({bundle:this.get("bundle")});
+ var bundleXml = bundleGenerator.process();
+ this.set("previewXml", vkbeautify.xml(bundleXml));
+ this.set("showingPreview", true);
+ },
+ importBundleTest(){
+ var deferred = this.importSampleBundle();
+ deferred.promise.then(function(data){
+ this.getBundleFromXml(data);
+ }.bind(this)).catch(function(e){
+ throw new Error(e);
+ });
+ },
+ openTab(type, path){
+ this.sendAction('openTab', type, path);
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/7c7412ed/contrib/views/wfmanager/src/main/resources/ui/app/components/bundle-coord-config.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/bundle-coord-config.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/bundle-coord-config.js
new file mode 100644
index 0000000..229b2cc
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/bundle-coord-config.js
@@ -0,0 +1,108 @@
+/*
+* 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.
+*/
+import Ember from 'ember';
+import { validator, buildValidations } from 'ember-cp-validations';
+
+const Validations = buildValidations({
+ 'coordinator.name': validator('presence', {
+ presence : true
+ }),
+ 'coordinator.appPath': validator('presence', {
+ presence : true
+ })
+});
+export default Ember.Component.extend(Validations, {
+ initialize : function(){
+ this.on('fileSelected',function(fileName){
+ this.set(this.get('filePathModel'), fileName);
+ }.bind(this));
+ this.set('showErrorMessage', false);
+ }.on('init'),
+ isValid(){
+ if(this.get('validations.isInvalid')) {
+ this.set('showErrorMessage', true);
+ return false;
+ }
+ return true;
+ },
+ readFromHdfs(filePath){
+ var url = Ember.ENV.API_URL + "/readWorkflowXml?workflowXmlPath="+filePath;
+ var deferred = Ember.RSVP.defer();
+ Ember.$.ajax({
+ url: url,
+ method: 'GET',
+ dataType: "text",
+ beforeSend: function (xhr) {
+ xhr.setRequestHeader("X-XSRF-HEADER", Math.round(Math.random()*100000));
+ xhr.setRequestHeader("X-Requested-By", "Ambari");
+ }
+ }).done(function(data){
+ deferred.resolve(data);
+ }).fail(function(){
+ deferred.reject();
+ });
+ return deferred;
+ },
+ importSampleCoordinator (){
+ var deferred = Ember.RSVP.defer();
+ Ember.$.ajax({
+ url: "/sampledata/coordinator.xml",
+ dataType: "text",
+ cache:false,
+ success: function(data) {
+ deferred.resolve(data);
+ }.bind(this),
+ failure : function(data){
+ deferred.reject(data);
+ }
+ });
+ return deferred;
+ },
+ actions : {
+ openFileBrowser(model){
+ this.set('filePathModel', model);
+ this.sendAction("openFileBrowser", model, this);
+ },
+ addCoordinator(){
+ if(this.isValid()){
+ this.sendAction('add');
+ }
+ },
+ updateCoordinator(){
+ if(this.isValid()){
+ this.sendAction('update');
+ }
+ },
+ cancelCoordinatorOperation(){
+ this.sendAction('cancel');
+ },
+ openTab(type, path){
+ this.sendAction('openTab', type, path);
+ },
+ showCoordinatorName(){
+ this.set('coordinatorName', null);
+ var deferred = this.readFromHdfs(this.get('coordinator.appPath'));
+ deferred.promise.then(function(data){
+ var x2js = new X2JS();
+ var coordJson = x2js.xml_str2json(data);
+ this.set('coordinatorName', coordJson["coordinator-app"]._name);
+ }.bind(this)).catch(function(){
+ this.set('coordinatorName', null);
+ }.bind(this));
+ }
+ }
+});