You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by nc...@apache.org on 2016/12/16 22:02:12 UTC

[04/51] [abbrv] ambari git commit: AMBARI-19031: UI Enhancements, import/export assets and smart version configuration (Padma Priya via nitirajrathore)

AMBARI-19031: UI Enhancements, import/export assets and smart version configuration (Padma Priya via nitirajrathore)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/fb567758
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/fb567758
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/fb567758

Branch: refs/heads/branch-dev-patch-upgrade
Commit: fb567758d71a4b90afa95d180f8340f6217f4f47
Parents: 8fc0ac7
Author: Nitiraj Rathore <ni...@gmail.com>
Authored: Fri Dec 16 15:03:57 2016 +0530
Committer: Nitiraj Rathore <ni...@gmail.com>
Committed: Fri Dec 16 15:03:57 2016 +0530

----------------------------------------------------------------------
 contrib/views/wfmanager/pom.xml                 |  29 +-
 .../apache/oozie/ambari/view/AmbariIOUtil.java  |  65 +++
 .../apache/oozie/ambari/view/HDFSFileUtils.java |  46 +-
 .../ambari/view/OozieProxyImpersonator.java     | 236 +++++++---
 .../apache/oozie/ambari/view/OozieUtils.java    |  52 +++
 .../oozie/ambari/view/WorkflowFileInfo.java     |  64 +++
 .../oozie/ambari/view/WorkflowFilesService.java | 116 +++++
 .../oozie/ambari/view/assets/AssetRepo.java     |  42 ++
 .../oozie/ambari/view/assets/AssetService.java  |  51 +++
 .../ambari/view/assets/model/ActionAsset.java   |  60 +++
 .../oozie/ambari/view/model/BaseModel.java      |  48 ++
 .../workflowmanager/WorkflowManagerService.java |  76 ++++
 .../WorkflowsManagerResource.java               |  51 +++
 .../view/workflowmanager/WorkflowsRepo.java     |  69 +++
 .../view/workflowmanager/model/Workflow.java    |  88 ++++
 .../ui/app/components/bundle-config.js          |  18 +-
 .../ui/app/components/bundle-coord-config.js    |   2 +-
 .../app/components/bundle-version-settings.js   |  49 +++
 .../resources/ui/app/components/coord-config.js |  30 +-
 .../ui/app/components/coord-version-settings.js |  49 +++
 .../ui/app/components/date-with-expr.js         |   1 -
 .../ui/app/components/designer-workspace.js     |  63 ++-
 .../ui/app/components/flow-designer.js          | 433 +++++++++++++++++--
 .../resources/ui/app/components/hdfs-browser.js |   3 +-
 .../ui/app/components/import-from-stream.js     |  67 +++
 .../resources/ui/app/components/job-config.js   |   2 +-
 .../resources/ui/app/components/job-details.js  |  28 +-
 .../main/resources/ui/app/components/job-row.js |   4 +
 .../ui/app/components/name-value-config.js      |   1 -
 .../ui/app/components/preview-dialog.js         |   7 +
 .../main/resources/ui/app/components/save-wf.js |  49 +--
 .../ui/app/components/search-create-new-bar.js  |  17 +-
 .../ui/app/components/workflow-actions.js       |   8 +
 .../ui/app/components/workflow-icon.js          |  21 +
 .../ui/app/controllers/design/dashboardtab.js   |  81 ++++
 .../ui/app/controllers/design/jobtab.js         |  59 +++
 .../main/resources/ui/app/controllers/job.js    |  30 +-
 .../app/domain/bundle/bundle-xml-generator.js   |   2 +-
 .../ui/app/domain/bundle/bundle-xml-importer.js |  20 +-
 .../coordinator/coordinator-xml-generator.js    |   2 +-
 .../coordinator/coordinator-xml-importer.js     |  22 +-
 .../ui/app/domain/cytoscape-flow-renderer.js    |  13 +-
 .../resources/ui/app/domain/mapping-utils.js    |   2 +-
 .../resources/ui/app/domain/schema-versions.js  | 165 ++++++-
 .../ui/app/domain/workflow-importer.js          |  35 +-
 .../ui/app/domain/workflow-xml-generator.js     |  15 +
 .../src/main/resources/ui/app/router.js         |   5 +-
 .../main/resources/ui/app/routes/dashboard.js   |   4 +-
 .../src/main/resources/ui/app/routes/design.js  |  15 +-
 .../ui/app/routes/design/dashboardtab.js        | 154 +++++++
 .../resources/ui/app/routes/design/jobtab.js    |  94 ++++
 .../src/main/resources/ui/app/routes/index.js   |   2 +-
 .../src/main/resources/ui/app/routes/job.js     |  29 +-
 .../ui/app/services/dashboard-context.js        |  38 ++
 .../main/resources/ui/app/services/save-job.js  |  42 ++
 .../src/main/resources/ui/app/styles/app.less   |  72 ++-
 .../app/templates/components/bundle-config.hbs  |  42 +-
 .../components/bundle-coord-config.hbs          |  17 +-
 .../templates/components/bundle-job-details.hbs |  12 +-
 .../components/bundle-version-settings.hbs      |  45 ++
 .../app/templates/components/coord-config.hbs   | 359 +++++++--------
 .../templates/components/coord-job-details.hbs  |   6 +-
 .../components/coord-version-settings.hbs       |  45 ++
 .../templates/components/designer-workspace.hbs | 119 ++---
 .../app/templates/components/flow-designer.hbs  | 154 +++++--
 .../app/templates/components/hdfs-browser.hbs   |   2 +-
 .../templates/components/import-from-stream.hbs |  63 +++
 .../ui/app/templates/components/job-config.hbs  |  10 +-
 .../ui/app/templates/components/job-details.hbs |  50 ++-
 .../ui/app/templates/components/job-row.hbs     |  14 +-
 .../app/templates/components/preview-dialog.hbs |   5 +-
 .../components/search-create-new-bar.hbs        |   2 +-
 .../app/templates/components/search-table.hbs   |   3 +-
 .../templates/components/workflow-actions.hbs   |   8 +-
 .../app/templates/components/workflow-icon.hbs  |  24 +
 .../components/workflow-job-details.hbs         |  28 +-
 .../main/resources/ui/app/templates/design.hbs  |   2 +-
 .../ui/app/templates/design/dashboardtab.hbs    |  30 ++
 .../ui/app/templates/design/jobtab.hbs          |  21 +
 .../src/main/resources/ui/app/templates/job.hbs |   2 +-
 .../main/resources/ui/app/utils/common-utils.js |   3 +
 .../main/resources/ui/app/utils/constants.js    |   2 +-
 .../wfmanager/src/main/resources/ui/bower.json  |   3 +-
 .../src/main/resources/ui/ember-cli-build.js    |   7 +
 .../src/main/resources/ui/package.json          |   4 +-
 .../components/bundle-version-settings-test.js  |  40 ++
 .../components/coord-version-settings-test.js   |  40 ++
 .../components/import-from-stream-test.js       |  41 ++
 .../components/workflow-icon-test.js            |  40 ++
 .../unit/routes/design/dashboardtab-test.js     |  27 ++
 .../unit/services/dashboard-context-test.js     |  28 ++
 .../ui/tests/unit/services/save-job-test.js     |  29 ++
 92 files changed, 3538 insertions(+), 635 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/contrib/views/wfmanager/pom.xml
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/pom.xml b/contrib/views/wfmanager/pom.xml
index d4aedf3..53d26a2 100644
--- a/contrib/views/wfmanager/pom.xml
+++ b/contrib/views/wfmanager/pom.xml
@@ -157,7 +157,7 @@
 			<plugin>
 				<groupId>com.github.eirslett</groupId>
 				<artifactId>frontend-maven-plugin</artifactId>
-				<version>0.0.16</version>
+				<version>1.1</version>
 				<configuration>
 					<nodeVersion>v4.5.0</nodeVersion>
 					<npmVersion>2.15.0</npmVersion>
@@ -213,12 +213,11 @@
 							<goal>exec</goal>
 						</goals>
 						<configuration>
-							<workingDirectory>${basedir}/src/main/resources/ui</workingDirectory>
-							<executable>node/node</executable>
+							<workingDirectory>${ui.directory}</workingDirectory>
+							<executable>${ui.directory}/node_modules/.bin/${ember.executable}</executable>
 							<arguments>
-								<argument>node_modules/.bin/ember</argument>
 								<argument>build</argument>
-
+								<argument>-prod</argument>
 							</arguments>
 						</configuration>
 					</execution>
@@ -246,24 +245,6 @@
 							<includeScope>runtime</includeScope>
 						</configuration>
 					</execution>
-					<execution>
-						<id>copy-artifact</id>
-						<phase>package</phase>
-						<goals>
-							<goal>copy</goal>
-						</goals>
-						<configuration>
-							<artifactItems>
-								<artifactItem>
-									<groupId>${project.groupId}</groupId>
-									<artifactId>${project.artifactId}</artifactId>
-									<version>${project.version}</version>
-									<type>${project.packaging}</type>
-								</artifactItem>
-							</artifactItems>
-							<outputDirectory>${views.jars.dir.rel}</outputDirectory>
-						</configuration>
-					</execution>
 				</executions>
 			</plugin>
 		</plugins>
@@ -303,6 +284,7 @@
         </activation>
         <properties>
           <node.executable>node.exe</node.executable>
+          <ember.executable>ember.cmd</ember.executable>
           <skip.nodegyp.chmod>true</skip.nodegyp.chmod>
         </properties>
       </profile>
@@ -315,6 +297,7 @@
         </activation>
         <properties>
           <node.executable>node</node.executable>
+          <ember.executable>ember</ember.executable>
           <skip.nodegyp.chmod>false</skip.nodegyp.chmod>
         </properties>
       </profile>

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/AmbariIOUtil.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/AmbariIOUtil.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/AmbariIOUtil.java
new file mode 100644
index 0000000..93447b7
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/AmbariIOUtil.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.ambari.view;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+import org.apache.ambari.view.URLStreamProvider;
+import org.apache.ambari.view.ViewContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AmbariIOUtil {
+	private final static Logger LOGGER = LoggerFactory
+			.getLogger(AmbariIOUtil.class);
+	private ViewContext viewContext;
+
+	public AmbariIOUtil(ViewContext viewContext) {
+		super();
+		this.viewContext = viewContext;
+	}
+
+	public 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);
+	}
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/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
index 58c3980..327d8fc 100644
--- 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
@@ -17,6 +17,7 @@
  */
 package org.apache.oozie.ambari.view;
 
+import java.io.FileNotFoundException;
 import java.io.IOException;
 
 import org.apache.ambari.view.ViewContext;
@@ -24,6 +25,7 @@ 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.apache.hadoop.fs.FileStatus;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -36,10 +38,10 @@ public class HDFSFileUtils {
 		super();
 		this.viewContext = viewContext;
 	}
+
 	public boolean fileExists(String path) {
-		boolean fileExists;
 		try {
-			fileExists = getHdfsgetApi().exists(path);
+			return getHdfsgetApi().exists(path);
 		} catch (IOException e) {
 			LOGGER.error(e.getMessage(), e);
 			throw new RuntimeException(e);
@@ -47,11 +49,9 @@ public class HDFSFileUtils {
 			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{
+
+	public FSDataInputStream read(String filePath) throws IOException {
 		FSDataInputStream is;
 		try {
 			is = getHdfsgetApi().open(filePath);
@@ -60,19 +60,28 @@ public class HDFSFileUtils {
 		}
 		return is;
 	}
-	public String createWorkflowFile( String workflowFile,String postBody,
-			boolean overwrite) throws IOException {
+
+	public String writeToFile(String filePath, String content, boolean overwrite)
+			throws IOException {
 		FSDataOutputStream fsOut;
 		try {
-			fsOut = getHdfsgetApi().create(workflowFile,
-					overwrite);
+			fsOut = getHdfsgetApi().create(filePath, overwrite);
 		} catch (InterruptedException e) {
 			throw new RuntimeException(e);
 		}
-		fsOut.write(postBody.getBytes());
+		fsOut.write(content.getBytes());
 		fsOut.close();
-		return workflowFile;
+		return filePath;
+	}
+
+	public void deleteFile(String filePath) throws IOException {
+		try {
+			getHdfsgetApi().delete(filePath, false);
+		} catch (InterruptedException e) {
+			throw new RuntimeException(e);
+		}
 	}
+
 	private HdfsApi getHdfsgetApi() {
 		try {
 			return HdfsUtil.connectToHDFSApi(viewContext);
@@ -84,4 +93,17 @@ public class HDFSFileUtils {
 		}
 	}
 
+	public FileStatus getFileStatus(String filePath) {
+		try {
+			return getHdfsgetApi().getFileStatus(filePath);
+		} catch (FileNotFoundException e) {
+			throw new RuntimeException(e);
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		} catch (InterruptedException e) {
+			throw new RuntimeException(e);
+		}
+
+	}
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/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 0533f04..08a166d 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
@@ -45,13 +45,13 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.StreamingOutput;
 import javax.ws.rs.core.UriInfo;
 
-import org.apache.ambari.view.URLStreamProvider;
 import org.apache.ambari.view.ViewContext;
 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.security.AccessControlException;
+import org.apache.oozie.ambari.view.workflowmanager.WorkflowManagerService;
+import org.apache.oozie.ambari.view.workflowmanager.WorkflowsManagerResource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -81,9 +81,13 @@ public class OozieProxyImpersonator {
 
 	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 Utils utils = new Utils();
+	private AmbariIOUtil ambariIOUtil;
+	private OozieUtils oozieUtils = new OozieUtils();
 	private HDFSFileUtils hdfsFileUtils;
+	private WorkflowFilesService workflowFilesService;
+	//private WorkflowManagerService workflowManagerService;
+
 	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(
@@ -111,10 +115,14 @@ public class OozieProxyImpersonator {
 	@Inject
 	public OozieProxyImpersonator(ViewContext viewContext) {
 		this.viewContext = viewContext;
-		hdfsFileUtils=new HDFSFileUtils(viewContext);
+		hdfsFileUtils = new HDFSFileUtils(viewContext);
+		workflowFilesService = new WorkflowFilesService(hdfsFileUtils);
+		ambariIOUtil=new AmbariIOUtil(viewContext);
+		//workflowManagerService = new WorkflowManagerService(viewContext);
 		LOGGER.info(String.format(
 				"OozieProxyImpersonator initialized for instance: %s",
 				viewContext.getInstanceName()));
+
 	}
 
 	@Path("/fileServices")
@@ -122,6 +130,11 @@ public class OozieProxyImpersonator {
 		return new FileServices(viewContext);
 	}
 
+   @Path("/wfprojects")
+    public WorkflowsManagerResource workflowsManagerResource() {
+        return new WorkflowsManagerResource(viewContext);
+    }
+	
 	@GET
 	@Path("/getCurrentUserName")
 	public Response getCurrentUserName() {
@@ -171,9 +184,12 @@ public class OozieProxyImpersonator {
 		}
 		postBody = utils.formatXml(postBody);
 		try {
-			String filePath = hdfsFileUtils.createWorkflowFile(getWorkflowFileName(appPath),postBody, overwrite);
+			String filePath = workflowFilesService.createWorkflowFile(appPath,
+					postBody, overwrite);
 			LOGGER.info(String.format(
 					"submit workflow job done. filePath=[%s]", filePath));
+		/*	workflowManagerService.saveWorkflow(appPath, JobType.WORKFLOW,
+                    "todo description", viewContext.getUsername());*/
 			return Response.ok().build();
 		} catch (Exception ex) {
 			LOGGER.error(ex.getMessage(), ex);
@@ -181,6 +197,125 @@ public class OozieProxyImpersonator {
 
 		}
 	}
+	
+	@POST
+	@Path("/publishAsset")
+	@Consumes({ MediaType.TEXT_PLAIN + "," + MediaType.TEXT_XML })
+	public Response publishAsset(String postBody, @Context HttpHeaders headers,
+			@Context UriInfo ui, @QueryParam("uploadPath") String uploadPath,
+			@DefaultValue("false") @QueryParam("overwrite") Boolean overwrite) {
+		LOGGER.info("publish asset called");
+		if (StringUtils.isEmpty(uploadPath)) {
+			throw new RuntimeException("upload path can't be empty.");
+		}
+		uploadPath = uploadPath.trim();
+		Response dryRunResponse = validateAsset(headers, postBody, ui.getQueryParameters());
+		if (dryRunResponse.getStatus() == 200) {
+			return saveAsset(postBody, uploadPath, overwrite);
+		}
+		return dryRunResponse;
+	}
+
+	private Response validateAsset(HttpHeaders headers, String postBody, MultivaluedMap<String, String> queryParams) {
+		String workflowXml = oozieUtils.generateWorkflowXml(postBody);	
+		try {
+			String tempWfPath = "/user/"+viewContext.getUsername()+"/tmpooziewfs/tempwf.xml";
+			hdfsFileUtils.writeToFile(tempWfPath, workflowXml, true);
+			queryParams.put("oozieparam.action",getAsList("dryrun"));
+			queryParams.put("oozieconfig.rerunOnFailure",getAsList("false"));
+			queryParams.put("oozieconfig.useSystemLibPath",getAsList("true"));
+			queryParams.put("resourceManager",getAsList("useDefault"));
+			String dryRunResp = submitWorkflowJobToOozie(headers,tempWfPath,queryParams,JobType.WORKFLOW);
+			LOGGER.info(String.format("resp from validating asset=[%s]",dryRunResp));
+			if (dryRunResp != null && dryRunResp.trim().startsWith("{")) {
+				return Response.status(Response.Status.OK).entity(dryRunResp).build();
+			} else {
+				HashMap<String, String> resp = new HashMap<String, String>();
+				resp.put("status", ErrorCodes.OOZIE_SUBMIT_ERROR.getErrorCode());
+				resp.put("message", dryRunResp);
+				//resp.put("stackTrace", dryRunResp);				
+				return Response.status(Response.Status.BAD_REQUEST).entity(resp).build();
+			}			
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	private List<String> getAsList(String string) {
+		ArrayList<String> li=new ArrayList<>(1);
+		li.add(string);
+		return li;
+	}
+
+	private Response saveAsset(String postBody, String uploadPath,
+			Boolean overwrite) {
+		uploadPath = workflowFilesService.getAssetFileName(uploadPath);
+		if (!overwrite) {
+			boolean fileExists = hdfsFileUtils.fileExists(uploadPath);
+			if (fileExists) {
+				return getFileExistsResponse();
+			}
+		}
+		postBody = utils.formatXml(postBody);
+		try {
+			String filePath = workflowFilesService.createAssetFile(uploadPath,
+					postBody, overwrite);
+			LOGGER.info(String.format(
+					"publish asset job done. filePath=[%s]", filePath));
+			return Response.ok().build();
+		} catch (Exception ex) {
+			LOGGER.error(ex.getMessage(), ex);
+			return getRespCodeForException(ex);
+		}
+	}
+
+	@POST
+	@Path("/saveWorkflowDraft")
+	@Consumes({ MediaType.TEXT_PLAIN + "," + MediaType.TEXT_XML })
+	public Response saveDraft(String postBody, @Context HttpHeaders headers,
+			@Context UriInfo ui, @QueryParam("app.path") String appPath,
+			@DefaultValue("false") @QueryParam("overwrite") Boolean overwrite)
+			throws IOException {
+		LOGGER.info("save workflow  called");
+		if (StringUtils.isEmpty(appPath)) {
+			throw new RuntimeException("app path can't be empty.");
+		}
+		appPath = appPath.trim();
+		workflowFilesService.saveDraft(appPath, postBody, overwrite);
+	/*	 workflowManagerService.saveWorkflow(appPath, JobType.WORKFLOW,
+	                "todo description", viewContext.getUsername());*/
+		return Response.ok().build();
+	}
+
+	@GET
+	@Path("/readWorkflowDraft")
+	public Response readDraft(@QueryParam("workflowXmlPath") String workflowPath) {
+		if (StringUtils.isEmpty(workflowPath)) {
+			throw new RuntimeException("workflowXmlPath can't be empty.");
+		}
+		try {
+			final InputStream is = workflowFilesService.readDraft(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);
+		}
+	}
+	
+	@POST
+	@Path("/discardWorkflowDraft")
+	public Response discardDraft(@QueryParam("workflowXmlPath") String workflowPath) throws IOException{
+		 workflowFilesService.discardDraft(workflowPath);
+		return Response.ok().build();
+	}
 
 	private Response submitJobInternal(String postBody, HttpHeaders headers,
 			UriInfo ui, String appPath, Boolean overwrite, JobType jobType) {
@@ -196,7 +331,8 @@ public class OozieProxyImpersonator {
 		}
 		postBody = utils.formatXml(postBody);
 		try {
-			String filePath = hdfsFileUtils.createWorkflowFile(getWorkflowFileName(appPath),postBody,overwrite);
+			String filePath = hdfsFileUtils.writeToFile(appPath, postBody,
+					overwrite);
 			LOGGER.info(String.format(
 					"submit workflow job done. filePath=[%s]", filePath));
 		} catch (Exception ex) {
@@ -204,6 +340,8 @@ public class OozieProxyImpersonator {
 			return getRespCodeForException(ex);
 
 		}
+		/* workflowManagerService.saveWorkflow(appPath, jobType,
+	                "todo description", viewContext.getUsername());*/
 		String response = submitWorkflowJobToOozie(headers, appPath,
 				ui.getQueryParameters(), jobType);
 		if (response != null && response.trim().startsWith("{")) {
@@ -226,21 +364,20 @@ public class OozieProxyImpersonator {
 					ErrorCodes.FILE_ACCESS_ACL_ERROR.getDescription(), ex);
 			return Response.status(Response.Status.BAD_REQUEST)
 					.entity(errorDetails).build();
-		}else if (ex instanceof IOException){
+		} 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 {
+		} 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() {
@@ -250,18 +387,14 @@ public class OozieProxyImpersonator {
 		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("/readWorkflowDetail")
+	public Response isDraftAvailable(@QueryParam("workflowXmlPath") String workflowPath){
+		WorkflowFileInfo workflowDetails = workflowFilesService.getWorkflowDetails(workflowPath);
+		return Response.ok(workflowDetails).build();
 	}
-
+	
 	@GET
 	@Path("/readWorkflowXml")
 	public Response readWorkflowXxml(
@@ -270,7 +403,7 @@ public class OozieProxyImpersonator {
 			throw new RuntimeException("workflowXmlPath can't be empty.");
 		}
 		try {
-			final FSDataInputStream is = hdfsFileUtils.read(workflowPath);
+			final InputStream is = workflowFilesService.readWorkflowXml(workflowPath);
 			StreamingOutput streamer = new StreamingOutput() {
 				@Override
 				public void write(OutputStream os) throws IOException,
@@ -281,7 +414,7 @@ public class OozieProxyImpersonator {
 				}
 			};
 			return Response.ok(streamer).status(200).build();
-		} catch(IOException e){
+		} catch (IOException e) {
 			return getRespCodeForException(e);
 		}
 	}
@@ -405,7 +538,7 @@ public class OozieProxyImpersonator {
 			queryParams.put("config.nameNode", nameNodes);
 		}
 
-		HashMap<String, String> workflowConigs = getWorkflowConfigs(filePath,
+		Map<String, String> workflowConigs = getWorkflowConfigs(filePath,
 				queryParams, jobType, nameNode);
 		String configXMl = oozieUtils.generateConfigXml(workflowConigs);
 		LOGGER.info("Config xml==" + configXMl);
@@ -415,7 +548,7 @@ public class OozieProxyImpersonator {
 				+ "/v2/jobs?" + getJobSumbitOozieParams(queryParams),
 				HttpMethod.POST, configXMl, customHeaders);
 
-		LOGGER.info("REsp from oozie status entity=="
+		LOGGER.info("Resp from oozie status entity=="
 				+ serviceResponse.getEntity());
 		if (serviceResponse.getEntity() instanceof String) {
 			return (String) serviceResponse.getEntity();
@@ -425,7 +558,7 @@ public class OozieProxyImpersonator {
 
 	}
 
-	private HashMap<String, String> getWorkflowConfigs(String filePath,
+	private Map<String, String> getWorkflowConfigs(String filePath,
 			MultivaluedMap<String, String> queryParams, JobType jobType,
 			String nameNode) {
 		HashMap<String, String> workflowConigs = new HashMap<String, String>();
@@ -466,7 +599,8 @@ public class OozieProxyImpersonator {
 			workflowConigs.put(OOZIE_WF_RERUN_FAILNODES_CONF_KEY, "true");
 		}
 		workflowConigs.put("user.name", viewContext.getUsername());
-		workflowConigs.put(oozieUtils.getJobPathPropertyKey(jobType), nameNode + filePath);
+		workflowConigs.put(oozieUtils.getJobPathPropertyKey(jobType), nameNode
+				+ filePath);
 		return workflowConigs;
 	}
 
@@ -497,10 +631,13 @@ public class OozieProxyImpersonator {
 		uiURI = uiURI.substring(index);
 		String serviceURI = getServiceUri();
 		serviceURI += uiURI;
-		MultivaluedMap<String, String> params = addOrReplaceUserName(ui.getQueryParameters());
-		return serviceURI+utils.convertParamsToUrl(params);
+		MultivaluedMap<String, String> params = addOrReplaceUserName(ui
+				.getQueryParameters());
+		return serviceURI + utils.convertParamsToUrl(params);
 	}
-	private MultivaluedMap<String, String> addOrReplaceUserName(MultivaluedMap<String, String> parameters){
+
+	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);
@@ -521,7 +658,7 @@ public class OozieProxyImpersonator {
 			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;
@@ -539,8 +676,8 @@ public class OozieProxyImpersonator {
 					.entity(stringResponse).type(MediaType.TEXT_PLAIN).build();
 		} else {
 			response = Response.status(Response.Status.OK)
-					.entity(stringResponse).type(utils.deduceType(stringResponse))
-					.build();
+					.entity(stringResponse)
+					.type(utils.deduceType(stringResponse)).build();
 		}
 		return response;
 	}
@@ -558,36 +695,7 @@ public class OozieProxyImpersonator {
 		}
 		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;
+		return ambariIOUtil.readFromUrl(urlToRead, method, body, newHeaders);
 	}
-
-	
-	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/fb567758/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
index 170132f..f002102 100644
--- 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
@@ -17,6 +17,8 @@
  */
 package org.apache.oozie.ambari.view;
 
+import java.io.IOException;
+import java.io.StringReader;
 import java.util.Map;
 
 import javax.xml.parsers.DocumentBuilder;
@@ -27,6 +29,8 @@ 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;
 
 public class OozieUtils {
 	private final static Logger LOGGER = LoggerFactory
@@ -68,4 +72,52 @@ public class OozieUtils {
 		}
 		throw new RuntimeException("Unknown Job Type");
 	}
+	
+	public String generateWorkflowXml(String actionNodeXml) {
+		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+		DocumentBuilder db;
+		try {
+			db = dbf.newDocumentBuilder();
+			Document doc = db.newDocument();
+			
+			Element workflowElement = doc.createElement("workflow-app");
+			workflowElement.setAttribute("name", "testWorkflow");
+			workflowElement.setAttribute("xmlns", "uri:oozie:workflow:0.5");
+			doc.appendChild(workflowElement);
+			
+			Element startElement = doc.createElement("start");
+			startElement.setAttribute("to", "testAction");
+			workflowElement.appendChild(startElement);
+			
+			Element actionElement = doc.createElement("action");
+			actionElement.setAttribute("name", "testAction");
+			Element actionSettingsElement = db.parse(new InputSource(new StringReader(actionNodeXml))).getDocumentElement();
+			actionElement.appendChild(doc.importNode(actionSettingsElement, true));
+			workflowElement.appendChild(actionElement);
+			
+			Element actionOkTransitionElement = doc.createElement("ok");
+			actionOkTransitionElement.setAttribute("to", "end");
+			actionElement.appendChild(actionOkTransitionElement);
+			
+			Element actionErrorTransitionElement = doc.createElement("error");
+			actionErrorTransitionElement.setAttribute("to", "kill");
+			actionElement.appendChild(actionErrorTransitionElement);
+			
+			Element killElement = doc.createElement("kill");
+			killElement.setAttribute("name", "kill");
+			Element killMessageElement = doc.createElement("message");
+			killMessageElement.setTextContent("Kill node message");
+			killElement.appendChild(killMessageElement);
+			workflowElement.appendChild(killElement);
+			
+			Element endElement = doc.createElement("end");
+			endElement.setAttribute("name", "end");
+			workflowElement.appendChild(endElement);
+			
+			return utils.generateXml(doc);
+		} catch (ParserConfigurationException | SAXException | IOException e) {
+			LOGGER.error("error in generating workflow xml", e);
+			throw new RuntimeException(e);
+		}
+	}
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/WorkflowFileInfo.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/WorkflowFileInfo.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/WorkflowFileInfo.java
new file mode 100644
index 0000000..6dbf30e
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/WorkflowFileInfo.java
@@ -0,0 +1,64 @@
+/**
+ * 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 class WorkflowFileInfo {
+	private String workflowPath;
+	private String draftPath;
+	private Boolean draftExists;
+	private Boolean isDraftCurrent=false;
+	public Boolean getIsDraftCurrent() {
+		return isDraftCurrent;
+	}
+	public void setIsDraftCurrent(Boolean isDraftCurrent) {
+		this.isDraftCurrent = isDraftCurrent;
+	}
+	private Long workflowModificationTime;
+	private Long draftModificationTime;
+	public String getWorkflowPath() {
+		return workflowPath;
+	}
+	public void setWorkflowPath(String workflowPath) {
+		this.workflowPath = workflowPath;
+	}
+	public String getDraftPath() {
+		return draftPath;
+	}
+	public void setDraftPath(String draftPath) {
+		this.draftPath = draftPath;
+	}
+	public Boolean getDraftExists() {
+		return draftExists;
+	}
+	public void setDraftExists(Boolean draftExists) {
+		this.draftExists = draftExists;
+	}
+	public void setWorkflowModificationTime(Long modificationTime) {
+		this.workflowModificationTime=modificationTime;
+	}
+	public void setDraftModificationTime(Long modificationTime) {
+		this.draftModificationTime=modificationTime;		
+	}
+	public Long getWorkflowModificationTime() {
+		return workflowModificationTime;
+	}
+	public Long getDraftModificationTime() {
+		return draftModificationTime;
+	}
+	
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/WorkflowFilesService.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/WorkflowFilesService.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/WorkflowFilesService.java
new file mode 100644
index 0000000..01bde47
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/WorkflowFilesService.java
@@ -0,0 +1,116 @@
+/**
+ * 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.ambari.view;
+
+import java.io.IOException;
+import java.io.InputStream;
+import org.apache.hadoop.fs.FileStatus;
+
+public class WorkflowFilesService {
+	private HDFSFileUtils hdfsFileUtils;
+
+	public WorkflowFilesService(HDFSFileUtils hdfsFileUtils) {
+		super();
+		this.hdfsFileUtils = hdfsFileUtils;
+	}
+
+	public String createWorkflowFile(String appPath, String content,
+			boolean overwrite) throws IOException {
+		return hdfsFileUtils.writeToFile(getWorkflowFileName(appPath), content,
+				overwrite);
+	}
+	
+	public String createAssetFile(String appPath, String content,
+			boolean overwrite) throws IOException {
+		return hdfsFileUtils.writeToFile(appPath, content,
+				overwrite);
+	}
+
+	public String saveDraft(String appPath, String content, boolean overwrite)
+			throws IOException {
+		return hdfsFileUtils.writeToFile(getWorkflowDrafFileName(appPath),
+				content, overwrite);
+	}
+
+	public InputStream readDraft(String appPath) throws IOException {
+		return hdfsFileUtils.read(getWorkflowDrafFileName(appPath));
+	}
+	public InputStream readWorkflowXml(String appPath) throws IOException {
+		return hdfsFileUtils.read(getWorkflowFileName(appPath));
+	}
+
+	private String getWorkflowDrafFileName(String appPath) {
+		return getWorkflowFileName(appPath).concat(".draft.json");
+	}
+
+	private String getWorkflowFileName(String appPath) {
+		String workflowFile = null;
+		if (appPath.endsWith(".xml")) {
+			workflowFile = appPath;
+		} else {
+			workflowFile = appPath + (appPath.endsWith("/") ? "" : "/")
+					+ "workflow.xml";
+		}
+		return workflowFile;
+	}
+	
+	public String getAssetFileName(String appPath) {
+		String assetFile = null;
+		if (appPath.endsWith(".xml")) {
+			assetFile = appPath;
+		} else {
+			assetFile = appPath + (appPath.endsWith("/") ? "" : "/")
+					+ "asset.xml";
+		}
+		return assetFile;
+	}
+
+	public void discardDraft(String workflowPath) throws IOException {
+		hdfsFileUtils.deleteFile(getWorkflowDrafFileName(workflowPath));
+
+	}
+
+	public WorkflowFileInfo getWorkflowDetails(String appPath) {
+		WorkflowFileInfo workflowInfo = new WorkflowFileInfo();
+		workflowInfo.setWorkflowPath(getWorkflowFileName(appPath));
+		boolean draftExists = hdfsFileUtils
+				.fileExists(getWorkflowDrafFileName(appPath));
+		workflowInfo.setDraftExists(draftExists);
+		boolean workflowExists = hdfsFileUtils.fileExists(getWorkflowFileName(appPath));
+		FileStatus workflowFileStatus = null;
+		if (workflowExists){
+			workflowFileStatus = hdfsFileUtils
+					.getFileStatus(getWorkflowFileName(appPath));
+			workflowInfo.setWorkflowModificationTime(workflowFileStatus
+					.getModificationTime());
+		}
+		if (draftExists) {
+			FileStatus draftFileStatus = hdfsFileUtils
+					.getFileStatus(getWorkflowDrafFileName(appPath));
+			workflowInfo.setDraftModificationTime(draftFileStatus
+					.getModificationTime());
+			if (!workflowExists){
+				workflowInfo.setIsDraftCurrent(true);
+			}else{
+				workflowInfo.setIsDraftCurrent(draftFileStatus.getModificationTime()
+						- workflowFileStatus.getModificationTime() > 0);
+			}
+		}
+		return workflowInfo;
+	}
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/assets/AssetRepo.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/assets/AssetRepo.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/assets/AssetRepo.java
new file mode 100644
index 0000000..1390ec0
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/assets/AssetRepo.java
@@ -0,0 +1,42 @@
+/**
+ * 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.ambari.view.assets;
+
+import java.util.Collection;
+
+import org.apache.ambari.view.DataStore;
+import org.apache.oozie.ambari.view.assets.model.ActionAsset;
+
+public class AssetRepo {
+    private final DataStore dataStore;
+
+    public AssetRepo(DataStore dataStore) {
+        super();
+        this.dataStore = dataStore;
+    }
+    public void saveAsset(ActionAsset asset){
+        
+    }
+    public void deleteAsset(ActionAsset asset){
+        
+    }
+    public  Collection<ActionAsset>listAllAssets(){
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/assets/AssetService.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/assets/AssetService.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/assets/AssetService.java
new file mode 100644
index 0000000..648a89f
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/assets/AssetService.java
@@ -0,0 +1,51 @@
+/**
+ * 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.ambari.view.assets;
+
+import java.util.Collection;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.oozie.ambari.view.assets.model.ActionAsset;
+
+public class AssetService {
+    private AssetRepo assetRepo;
+
+    public AssetService(ViewContext viewContext) {
+        super();
+        assetRepo = new AssetRepo(viewContext.getDataStore());
+    }
+
+    public Collection<ActionAsset> getAssets() {
+
+        return null;
+    }
+
+    public Collection<ActionAsset> getPrioritizedAssets() {
+        Collection<ActionAsset> assets = getAssets();
+        // reorder
+        return assets;
+    }
+
+    public void addAsset() {
+
+    }
+
+    public void deleteAsset() {
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/assets/model/ActionAsset.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/assets/model/ActionAsset.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/assets/model/ActionAsset.java
new file mode 100644
index 0000000..8eb6081
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/assets/model/ActionAsset.java
@@ -0,0 +1,60 @@
+/**
+ * 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.ambari.view.assets.model;
+
+import org.apache.oozie.ambari.view.model.BaseModel;
+
+public class ActionAsset extends BaseModel {
+    private String id;
+    private String name;
+    private String description;
+    private String assetLocation;
+    private String type;
+
+    public String getId() {
+        return id;
+    }
+    public void setId(String id) {
+        this.id = id;
+    }
+    public String getName() {
+        return name;
+    }
+    public void setName(String name) {
+        this.name = name;
+    }
+    public String getDescription() {
+        return description;
+    }
+    public void setDescription(String description) {
+        this.description = description;
+    }
+  
+    public String getType() {
+        return type;
+    }
+    public void setType(String type) {
+        this.type = type;
+    }
+    public String getAssetLocation() {
+        return assetLocation;
+    }
+    public void setAssetLocation(String assetLocation) {
+        this.assetLocation = assetLocation;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/model/BaseModel.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/model/BaseModel.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/model/BaseModel.java
new file mode 100644
index 0000000..553ce24
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/model/BaseModel.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.ambari.view.model;
+
+import java.io.Serializable;
+
+
+public class BaseModel implements Serializable{
+    private static final long serialVersionUID = 1L;
+    private String createdAt;
+    private String updatedAt;
+    private String owner;
+    
+    public String getCreatedAt() {
+        return createdAt;
+    }
+    public void setCreatedAt(String createdAt) {
+        this.createdAt = createdAt;
+    }
+    public String getUpdatedAt() {
+        return updatedAt;
+    }
+    public void setUpdatedAt(String updatedAt) {
+        this.updatedAt = updatedAt;
+    }
+    public String getOwner() {
+        return owner;
+    }
+    public void setOwner(String owner) {
+        this.owner = owner;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/WorkflowManagerService.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/WorkflowManagerService.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/WorkflowManagerService.java
new file mode 100644
index 0000000..4c88454
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/WorkflowManagerService.java
@@ -0,0 +1,76 @@
+/**
+ * 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.ambari.view.workflowmanager;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Date;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.oozie.ambari.view.HDFSFileUtils;
+import org.apache.oozie.ambari.view.JobType;
+import org.apache.oozie.ambari.view.workflowmanager.model.Workflow;
+
+public class WorkflowManagerService {
+
+    private WorkflowsRepo workflowsRepository;
+    private HDFSFileUtils hdfsFileUtils;
+
+    public WorkflowManagerService(ViewContext viewContext) {
+        workflowsRepository = new WorkflowsRepo(viewContext.getDataStore());
+        hdfsFileUtils = new HDFSFileUtils(viewContext);
+    }
+
+    public void saveWorkflow(String path, JobType jobType, String descripton,
+            String userName) {
+        // workflowsRepository.getWorkflow(path);
+        Workflow workflowByPath = getWorkflowByPath(path);
+        if (workflowByPath == null) {
+            Workflow wf = new Workflow();
+            wf.setOwner(userName);
+            wf.setType(jobType.name());
+            wf.setWorkflowDefinitionPath(path);
+            Date now = new Date();
+            wf.setUpdatedAt(String.valueOf(now.getTime()));
+            workflowsRepository.updateWorkflow(wf);
+        } else {
+            Date now = new Date();
+            workflowByPath.setUpdatedAt(String.valueOf(now.getTime()));
+            workflowsRepository.updateWorkflow(workflowByPath);
+        }
+    }
+
+    public Collection<Workflow> getAllWorkflows() {
+        return workflowsRepository.getAllWorkflows();
+    }
+
+    public Workflow getWorkflowByPath(String path) {
+        return workflowsRepository.getWorkflow(path);
+    }
+
+    public void deleteWorkflow(String path, Boolean deleteDefinition) {
+        if (deleteDefinition) {
+            try {
+                hdfsFileUtils.deleteFile(path);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        workflowsRepository.deleteWorkflow(path);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/WorkflowsManagerResource.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/WorkflowsManagerResource.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/WorkflowsManagerResource.java
new file mode 100644
index 0000000..17a3296
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/WorkflowsManagerResource.java
@@ -0,0 +1,51 @@
+/**
+ * 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.ambari.view.workflowmanager;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.QueryParam;
+
+import org.apache.ambari.view.ViewContext;
+
+public class WorkflowsManagerResource {
+	private WorkflowManagerService workflowManagerService;
+	
+	public WorkflowsManagerResource(ViewContext viewContext) {
+		super();
+		this.workflowManagerService=new WorkflowManagerService(viewContext);
+	}
+
+	@GET
+	public Map<String,Object> getWorkflows(){
+	    HashMap<String,Object> result=new HashMap<>();
+	    result.put("wfprojects", workflowManagerService.getAllWorkflows());
+	    return result;
+	}
+	
+	
+	@DELETE
+	public void deleteWorkflow( @QueryParam("worfkflowPath") String path,
+            @DefaultValue("false") @QueryParam("deleteDefinition") Boolean deleteDefinition){
+	    workflowManagerService.deleteWorkflow(path,deleteDefinition);
+	}
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/WorkflowsRepo.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/WorkflowsRepo.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/WorkflowsRepo.java
new file mode 100644
index 0000000..978c059
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/WorkflowsRepo.java
@@ -0,0 +1,69 @@
+/**
+ * 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.ambari.view.workflowmanager;
+
+import java.util.Collection;
+
+import org.apache.ambari.view.DataStore;
+import org.apache.ambari.view.PersistenceException;
+import org.apache.oozie.ambari.view.workflowmanager.model.Workflow;
+
+public class WorkflowsRepo {
+    private final DataStore dataStore;
+
+    public WorkflowsRepo(DataStore dataStore) {
+        super();
+        this.dataStore=dataStore;
+    }
+    public Collection<Workflow> getAllWorkflows(){
+        try {
+            return dataStore.findAll(Workflow.class,null);
+        } catch (PersistenceException e) {
+           throw new RuntimeException(e);
+        }
+    }
+    public void deleteWorkflow(String workflowPath){
+        try {
+            Workflow workflow = this.getWorkflow(workflowPath);
+            this.dataStore.remove(workflow);
+        } catch (PersistenceException e) {
+           throw new RuntimeException(e);
+        }
+    }
+    public void createWorkflow(Workflow wf){
+        try {
+            this.dataStore.store(wf);
+        } catch (PersistenceException e) {
+           throw new RuntimeException(e);
+        }
+    }
+    public void updateWorkflow(Workflow wf){
+        try {
+            this.dataStore.store(wf);
+        } catch (PersistenceException e) {
+           throw new RuntimeException(e);
+        }
+    }
+    public Workflow getWorkflow(String path) {
+        try {
+            return this.dataStore.find(Workflow.class, "workflowDefinitionPath='"+path+"'");
+        } catch (PersistenceException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/model/Workflow.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/model/Workflow.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/model/Workflow.java
new file mode 100644
index 0000000..ad5f48b
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/model/Workflow.java
@@ -0,0 +1,88 @@
+/**
+ * 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.workflowmanager.model;
+
+import org.apache.oozie.ambari.view.model.BaseModel;
+
+public class Workflow extends BaseModel{
+    private static final long serialVersionUID = 1L;
+    private String id = null;
+    private String name;
+    private String desciption;
+    private String workflowDefinitionPath;
+    private String type;
+    private String isDraft;
+    private String definitionMissing;//true or not if path is fine. 
+    
+    public String getType() {
+        return type;
+    }
+    
+    public void setType(String type) {
+        this.type = type;
+    }
+    
+    public String getId() {
+        return id;
+    }
+    
+    public void setId(String id) {
+        this.id = id;
+    }
+    
+    public String getName() {
+        return name;
+    }
+    
+    public void setName(String name) {
+        this.name = name;
+    }
+    
+    public String getWorkflowDefinitionPath() {
+        return workflowDefinitionPath;
+    }
+    
+    public void setWorkflowDefinitionPath(String workflowDefinitionPath) {
+        this.workflowDefinitionPath = workflowDefinitionPath;
+    }
+   
+    public String getIsDraft() {
+        return isDraft;
+    }
+   
+    public void setIsDraft(String isDraft) {
+        this.isDraft = isDraft;
+    }
+    
+    public String getDesciption() {
+        return desciption;
+    }
+    
+    public void setDesciption(String desciption) {
+        this.desciption = desciption;
+    }
+   
+    public String getDefinitionMissing() {
+        return definitionMissing;
+    }
+
+    public void setDefinitionMissing(String definitionMissing) {
+        this.definitionMissing = definitionMissing;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/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
index 2799db5..4f409a1 100644
--- 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
@@ -20,6 +20,7 @@ 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';
+import SchemaVersions from '../domain/schema-versions';
 
 const Validations = buildValidations({
   'bundle.name': validator('presence', {
@@ -41,6 +42,8 @@ const Validations = buildValidations({
 
 export default Ember.Component.extend(Ember.Evented, Validations, {
   bundle : null,
+  errors: Ember.A([]),
+  schemaVersions : SchemaVersions.create({}),
   propertyExtractor : Ember.inject.service('property-extractor'),
   fileBrowser : Ember.inject.service('file-browser'),
   workspaceManager : Ember.inject.service('workspace-manager'),
@@ -107,7 +110,8 @@ export default Ember.Component.extend(Ember.Evented, Validations, {
         displayValue : '',
         type : 'date'
       },
-      coordinators : null
+      coordinators : null,
+      schemaVersions : this.get("schemaVersions")
     });
   },
   importSampleBundle (){
@@ -156,9 +160,11 @@ export default Ember.Component.extend(Ember.Evented, Validations, {
     return deferred;
   },
   getBundleFromXml(bundleXml){
-    var bundleXmlImporter = BundleXmlImporter.create({});
-    var bundle = bundleXmlImporter.importBundle(bundleXml);
-    this.set("bundle", bundle);
+    var bundleXmlImporter = BundleXmlImporter.create({schemaVersions: this.get("schemaVersions")});
+    var bundleObj = bundleXmlImporter.importBundle(bundleXml, this.get("errors"));
+    this.set("bundle", bundleObj.bundle);
+    this.get("errors").clear();
+    this.get("errors").pushObjects(bundleObj.errors);
   },
   actions : {
     closeFileBrowser(){
@@ -220,6 +226,7 @@ export default Ember.Component.extend(Ember.Evented, Validations, {
     },
     resetBundle(){
       this.set('bundle', this.createBundle());
+      this.set('errors').clear();
     },
     closeBundleSubmitConfig(){
       this.set("showingJobConfig", false);
@@ -257,6 +264,9 @@ export default Ember.Component.extend(Ember.Evented, Validations, {
     },
     openTab(type, path){
       this.sendAction('openTab', type, path);
+    },
+    showVersionSettings(value){
+      this.set('showVersionSettings', value);
     }
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/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
index 229b2cc..2280f82 100644
--- 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
@@ -99,7 +99,7 @@ export default Ember.Component.extend(Validations, {
       deferred.promise.then(function(data){
         var x2js = new X2JS();
         var coordJson = x2js.xml_str2json(data);
-        this.set('coordinatorName', coordJson["coordinator-app"]._name);
+        this.set('coordinator.name', coordJson["coordinator-app"]._name);
       }.bind(this)).catch(function(){
         this.set('coordinatorName', null);
       }.bind(this));

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/contrib/views/wfmanager/src/main/resources/ui/app/components/bundle-version-settings.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/bundle-version-settings.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/bundle-version-settings.js
new file mode 100644
index 0000000..3bd6653
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/bundle-version-settings.js
@@ -0,0 +1,49 @@
+/*
+*    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 Constants from '../utils/constants';
+
+export default Ember.Component.extend({
+  initialize : function(){
+    this.set('currentBundleVersion', this.get('schemaVersions').getCurrentBundleVersion());
+    this.set('bundleSchemaVersions', this.get('schemaVersions').getBundleVersions());
+    this.get('schemaVersions').createCopy();
+  }.on('init'),
+  BundleVersionObserver : Ember.observer('currentBundleVersion',function(){
+    this.get('schemaVersions').setCurrentBundleVersion(this.get('currentBundleVersion'));
+  }),
+  rendered : function(){
+    this.$('#version-settings-dialog').modal({
+      backdrop: 'static',
+      keyboard: false
+    });
+    this.$('#version-settings-dialog').modal('show');
+    this.$('#version-settings-dialog').modal().on('hidden.bs.modal', function() {
+      this.sendAction('showVersionSettings', false);
+    }.bind(this));
+  }.on('didInsertElement'),
+  actions : {
+    save (){
+      this.$('#version-settings-dialog').modal('hide');
+    },
+    cancel (){
+      this.get('schemaVersions').rollBack();
+      this.$('#version-settings-dialog').modal('hide');
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/contrib/views/wfmanager/src/main/resources/ui/app/components/coord-config.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/coord-config.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/coord-config.js
index 57fcdf8..23d1955 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/components/coord-config.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/coord-config.js
@@ -19,6 +19,7 @@ import {Coordinator} from '../domain/coordinator/coordinator';
 import {CoordinatorGenerator} from '../domain/coordinator/coordinator-xml-generator';
 import {CoordinatorXmlImporter} from '../domain/coordinator/coordinator-xml-importer';
 import {SlaInfo} from '../domain/sla-info';
+import SchemaVersions from '../domain/schema-versions';
 import Constants from '../utils/constants';
 import { validator, buildValidations } from 'ember-cp-validations';
 
@@ -42,6 +43,8 @@ const Validations = buildValidations({
 
 export default Ember.Component.extend(Validations, Ember.Evented, {
   coordinator : null,
+  errors: Ember.A([]),
+  schemaVersions : SchemaVersions.create({}),
   childComponents : new Map(),
   fileBrowser : Ember.inject.service('file-browser'),
   propertyExtractor : Ember.inject.service('property-extractor'),
@@ -204,18 +207,20 @@ export default Ember.Component.extend(Validations, Ember.Evented, {
         }
       },
       controls : Ember.A([]),
-      slainfo : SlaInfo.create({})
+      slainfo : SlaInfo.create({}),
+      schemaVersions : this.get("schemaVersions")
     });
   },
   importSampleCoordinator (){
+    var self = this;
     var deferred = Ember.RSVP.defer();
     Ember.$.ajax({
       url: "/sampledata/coordinator.xml",
       dataType: "text",
       cache:false,
       success: function(data) {
-        var coordinatorXmlImporter = CoordinatorXmlImporter.create({});
-        var coordinator = coordinatorXmlImporter.importCoordinator(data);
+        var coordinatorXmlImporter = CoordinatorXmlImporter.create({schemaVersions: self.schemaVersions});
+        var coordinator = coordinatorXmlImporter.importCoordinator(data, self.errors);
         deferred.resolve(coordinator);
       }.bind(this),
       failure : function(data){
@@ -270,9 +275,12 @@ export default Ember.Component.extend(Validations, Ember.Evented, {
     return deferred;
   },
   getCoordinatorFromXml(coordinatorXml){
-    var coordinatorXmlImporter = CoordinatorXmlImporter.create({});
-    var coordinator = coordinatorXmlImporter.importCoordinator(coordinatorXml);
+    var coordinatorXmlImporter = CoordinatorXmlImporter.create({schemaVersions: this.get("schemaVersions")});
+    var coordinatorObj = coordinatorXmlImporter.importCoordinator(coordinatorXml, this.errors);
+    var coordinator = coordinatorObj.coordinator;
     this.set("coordinator", coordinator);
+    this.get("errors").clear();
+    this.get("errors").pushObjects(coordinatorObj.errors);
     this.$('input[name="dataInputType"][value="'+ coordinator.get('dataInputType')+'"]').prop('checked', true);
     if(coordinator.get('dataInputType') === 'logical'){
       this.set('conditionalDataInExists', true);
@@ -450,13 +458,16 @@ export default Ember.Component.extend(Validations, Ember.Evented, {
     },
     resetCoordinator(){
       this.set('coordinator', this.createNewCoordinator());
+      this.get("errors").clear();
     },
     importCoordinatorTest(){
       var deferred = this.importSampleCoordinator();
       deferred.promise.then(function(data){
-        this.set("coordinator", data);
-        this.$('input[name="dataInputType"][value="'+ data.get('dataInputType')+'"]').prop('checked', true);
-        if(data.get('dataInputType') === 'logical'){
+        this.set("coordinator", data.coordinator);
+        this.get("errors").clear();
+        this.get("errors").pushObjects(data.errors);
+        this.$('input[name="dataInputType"][value="'+ data.coordinator.get('dataInputType')+'"]').prop('checked', true);
+        if(data.coordinator.get('dataInputType') === 'logical'){
           this.set('conditionalDataInExists', true);
         }
         console.error(this.get('coordinator'));
@@ -516,6 +527,9 @@ export default Ember.Component.extend(Validations, Ember.Evented, {
       }.bind(this)).catch(function(){
         this.set('workflowName', null);
       }.bind(this));
+    },
+    showVersionSettings(value){
+      this.set('showVersionSettings', value);
     }
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/contrib/views/wfmanager/src/main/resources/ui/app/components/coord-version-settings.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/coord-version-settings.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/coord-version-settings.js
new file mode 100644
index 0000000..6385a44
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/coord-version-settings.js
@@ -0,0 +1,49 @@
+/*
+*    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 Constants from '../utils/constants';
+
+export default Ember.Component.extend({
+  initialize : function(){
+    this.set('currentCoordinatorVersion', this.get('schemaVersions').getCurrentCoordinatorVersion());
+    this.set('coordinatorSchemaVersions', this.get('schemaVersions').getCoordinatorVersions());
+    this.get('schemaVersions').createCopy();
+  }.on('init'),
+  CoordinatorVersionObserver : Ember.observer('currentCoordinatorVersion',function(){
+    this.get('schemaVersions').setCurrentCoordinatorVersion(this.get('currentCoordinatorVersion'));
+  }),
+  rendered : function(){
+    this.$('#version-settings-dialog').modal({
+      backdrop: 'static',
+      keyboard: false
+    });
+    this.$('#version-settings-dialog').modal('show');
+    this.$('#version-settings-dialog').modal().on('hidden.bs.modal', function() {
+      this.sendAction('showVersionSettings', false);
+    }.bind(this));
+  }.on('didInsertElement'),
+  actions : {
+    save (){
+      this.$('#version-settings-dialog').modal('hide');
+    },
+    cancel (){
+      this.get('schemaVersions').rollBack();
+      this.$('#version-settings-dialog').modal('hide');
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/contrib/views/wfmanager/src/main/resources/ui/app/components/date-with-expr.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/date-with-expr.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/date-with-expr.js
index dd9a9ae..541104d 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/components/date-with-expr.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/date-with-expr.js
@@ -55,7 +55,6 @@ export default Ember.Component.extend(Validations, {
         useCurrent: false,
         showClose : true
       });
-      this.set('dateField.displayValue', undefined);
     }else{
       var dateTimePicker = this.$('input[name="'+this.get('inputName')+'"]').data("DateTimePicker");
       if(dateTimePicker){

http://git-wip-us.apache.org/repos/asf/ambari/blob/fb567758/contrib/views/wfmanager/src/main/resources/ui/app/components/designer-workspace.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/designer-workspace.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/designer-workspace.js
index a3d64b0..6cbc3dd 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/components/designer-workspace.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/designer-workspace.js
@@ -15,7 +15,7 @@
 *    limitations under the License.
 */
 import Ember from 'ember';
-
+import CommonUtils from "../utils/common-utils";
 export default Ember.Component.extend({
   workspaceManager : Ember.inject.service('workspace-manager'),
   xmlAppPath : null,
@@ -42,10 +42,22 @@ export default Ember.Component.extend({
     this.get('tabs').forEach((tab)=>{
       this.get('tabCounter').set(tab.type, (this.get('tabCounter').get(tab.type)) + 1);
     }, this);
+
+    Ember.getOwner(this).lookup('route:design').on('openNewTab', function(path){
+      this.createNewTab('wf', path);
+    }.bind(this));
+
   }.on('init'),
   elementsInserted : function(){
     this.$('.nav-tabs a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
-      this.get('workspaceManager').setLastActiveTab((this.$(e.target).attr('href').slice(1)));
+      var id = this.$(e.target).attr('href').slice(1);
+      this.get('workspaceManager').setLastActiveTab(id);
+      var tab = this.get('tabs').findBy('id', id);
+      if(tab.type === 'dashboard'){
+        this.sendAction('showDashboard');
+      }else{
+        this.sendAction('hideDashboard');
+      }
     }.bind(this));
 
     if(this.get('tabs') && this.get('tabs').length > 0){
@@ -54,19 +66,13 @@ export default Ember.Component.extend({
       if(!activeTab){
         activeTab = this.get('tabs').objectAt(this.get('tabs').length - 1);
       }
-      this.$('.nav-tabs a[href="#' + activeTab.id + '"]').tab('show');
-    }else{
-      if(this.get('hasMultitabSupport')){
-        this.createNewTab(this.get('type'), this.get('xmlAppPath'));
+      if(activeTab.type === 'dashboard'){
+        this.createOrshowDashboard();
       }else{
-         var tab = this.get('tabs').findBy('type', this.get('type'));
-        if(!tab){
-          this.createNewTab(this.get('type'), this.get('xmlAppPath'));
-        }else{
-          Ember.set(tab,'path', this.get('xmlAppPath'));
-          this.$('.nav-tabs a[href="#' + tab.id + '"]').tab('show');
-        }
+        this.$('.nav-tabs a[href="#' + activeTab.id + '"]').tab('show');
       }
+    }else{
+      this.createOrshowDashboard();
     }
   }.on('didInsertElement'),
   onDestroy : function(){
@@ -100,6 +106,23 @@ export default Ember.Component.extend({
     this.get('tabCounter').set(type, ++count);
     return count;
   },
+  createOrshowDashboard(){
+    var dashboardTab = this.get('tabs').findBy('type', 'dashboard');
+    if(dashboardTab && dashboardTab.type === 'dashboard'){
+      this.$('.nav-tabs a[href="#' + dashboardTab.id + '"]').tab('show');
+    }else{
+      var tab = {
+        type : 'dashboard',
+        id : this.generateTabId(),
+        name : 'Dashboard'
+      };
+      this.$('.nav-tabs li').removeClass('active');
+      this.$('.tab-content .tab-pane').removeClass('active');
+      this.get('tabs').pushObject(tab);
+      this.$('.nav-tabs a[href="#' + tab.id + '"]').tab('show');
+    }
+    this.sendAction('showDashboard');
+  },
   generateTabId(){
     return 'tab-'+ Math.ceil(Math.random() * 100000);
   },
@@ -109,6 +132,7 @@ export default Ember.Component.extend({
       Ember.set(tab, 'context', context);
     },
     show(type){
+      this.sendAction('hideDashboard');
       if(this.get('hasMultitabSupport')){
         this.createNewTab(type);
       }else{
@@ -120,6 +144,9 @@ export default Ember.Component.extend({
         }
       }
     },
+    showDashboard(){
+      this.createOrshowDashboard();
+    },
     closeTab(index){
       if(index < this.get('tabs').length - 1){
         var previousTab = this.get('tabs').objectAt(index + 1);
@@ -127,6 +154,13 @@ export default Ember.Component.extend({
       }
       this.get('workspaceManager').deleteWorkInProgress(this.get('tabs').objectAt(index).id);
       this.get('tabs').removeAt(index);
+      Ember.run.later(()=>{
+        var type = this.$('.nav-tabs').find('.active').attr('data-type');
+        console.error(type);
+        if(type === 'dashboard'){
+          this.createOrshowDashboard();
+        }
+      }.bind(this));
     },
     openTab(type, path){
       if(this.get('hasMultitabSupport')){
@@ -151,7 +185,10 @@ export default Ember.Component.extend({
     },
     interceptShow(tab){
       if(tab.type === 'wf' && tab.context){
+        CommonUtils.setTestContext(tab.context);
         tab.context.resize();
+      }else if(tab.type === 'dashboard'){
+        this.sendAction('showDashboard');
       }
     }
   }