airavata-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sma...@apache.org
Subject [airavata-sandbox] 02/19: Integrated workflow creation flow
Date Wed, 06 Dec 2017 03:13:46 GMT
This is an automated email from the ASF dual-hosted git repository.

smarru pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-sandbox.git

commit e20636abffeee060ccd82fcc32153c6d0591158f
Author: dimuthu.upeksha2@gmail.com <Dimu@1234>
AuthorDate: Wed Nov 8 06:36:48 2017 +0530

    Integrated workflow creation flow
---
 .../k8s/api/resources/process/ProcessResource.java |    30 +
 .../k8s/api/resources/task/TaskInputResource.java  |    61 +
 .../api/resources/task/TaskOutPortResource.java    |    40 +
 .../k8s/api/resources/task/TaskOutputResource.java |    61 +
 .../k8s/api/resources/task/TaskResource.java       |    68 +-
 .../resources/task/type/TaskInputTypeResource.java |    51 +
 .../task/type/TaskOutPortTypeResource.java         |    41 +
 .../task/type/TaskOutputTypeResource.java          |    41 +
 .../api/resources/task/type/TaskTypeResource.java  |    84 +
 .../api/resources/workflow/WorkflowResource.java   |    54 +
 .../k8s/api/server/controller/TaskController.java  |     2 +-
 .../api/server/controller/TaskTypeController.java  |    33 +
 .../api/server/controller/WorkflowController.java  |    39 +
 .../k8s/api/server/model/process/ProcessModel.java |    66 +-
 .../k8s/api/server/model/task/TaskDAG.java         |    52 +
 .../k8s/api/server/model/task/TaskInput.java       |    86 +
 .../k8s/api/server/model/task/TaskModel.java       |   110 +-
 .../k8s/api/server/model/task/TaskOutPort.java     |    57 +
 .../k8s/api/server/model/task/TaskOutput.java      |    86 +
 .../k8s/api/server/model/task/TaskParam.java       |    82 -
 .../api/server/model/task/type/TaskInputType.java  |    69 +
 .../api/server/model/task/type/TaskModelType.java  |    95 +
 .../server/model/task/type/TaskOutPortType.java    |    61 +
 .../api/server/model/task/type/TaskOutputType.java |    59 +
 .../k8s/api/server/model/workflow/Workflow.java    |    69 +
 .../api/server/repository/TaskParamRepository.java |    32 -
 .../ApplicationDeploymentRepository.java           |     2 +-
 .../ApplicationIfaceRepository.java                |     2 +-
 .../ApplicationInputRepository.java                |     2 +-
 .../ApplicationModuleRepository.java               |     2 +-
 .../ApplicationOutputRepository.java               |     2 +-
 .../{ => compute}/ComputeRepository.java           |     2 +-
 .../repository/{ => data}/DataStoreRepository.java |     2 +-
 .../ExperimentInputDataRepository.java             |     2 +-
 .../ExperimentOutputDataRepository.java            |     2 +-
 .../{ => experiment}/ExperimentRepository.java     |     2 +-
 .../ExperimentStatusRepository.java                |     2 +-
 .../{ => process}/ProcessRepository.java           |     2 +-
 .../{ => process}/ProcessStatusRepository.java     |     2 +-
 .../server/repository/task/TaskDAGRepository.java  |    13 +
 .../repository/task/TaskInputRepository.java       |    13 +
 .../repository/task/TaskOutPortRepository.java     |    17 +
 .../repository/task/TaskOutputRepository.java      |    13 +
 .../repository/{ => task}/TaskRepository.java      |     2 +-
 .../{ => task}/TaskStatusRepository.java           |     2 +-
 .../task/type/TaskInputTypeRepository.java         |    13 +
 .../task/type/TaskOutPortTypeRepository.java       |    13 +
 .../task/type/TaskOutputTypeRepository.java        |    13 +
 .../repository/task/type/TaskTypeRepository.java   |    17 +
 .../repository/workflow/WorkflowRepository.java    |    16 +
 .../service/ApplicationDeploymentService.java      |     6 +-
 .../server/service/ApplicationIfaceService.java    |     8 +-
 .../server/service/ApplicationModuleService.java   |     2 +-
 .../api/server/service/ComputeResourceService.java |     2 +-
 .../k8s/api/server/service/ExperimentService.java  |     7 +-
 .../k8s/api/server/service/ProcessService.java     |    25 +-
 .../k8s/api/server/service/WorkflowService.java    |   129 +
 .../api/server/service/data/DataStoreService.java  |     6 +-
 .../api/server/service/{ => task}/TaskService.java |    73 +-
 .../server/service/task/type/TaskTypeService.java  |    94 +
 .../k8s/api/server/service/util/GraphParser.java   |   440 +
 .../api/server/service/util/ToResourceUtil.java    |   148 +-
 .../k8s/gfac/core/ProcessLifeCycleManager.java     |    27 -
 .../pom.xml                                        |     7 +-
 .../apache/airavata/k8s/task/job/Application.java  |     8 +-
 .../airavata/k8s/task/job/CommandTaskInfo.java     |    59 +
 .../airavata/k8s/task/job/config/RestConfig.java   |    10 +
 .../k8s/task/job/service/TaskExecutionService.java |   100 +
 .../src/main/resources/application.properties      |     0
 .../src/main/resources/application.yml             |     0
 .../pom.xml                                        |     7 +-
 .../airavata/k8s/task/egress/Application.java      |     6 +
 .../k8s/task/egress/DataCollectingTaskInfo.java    |    50 +
 .../task/egress/service/TaskExecutionService.java  |   107 +
 .../src/main/resources/application.properties      |     0
 .../src/main/resources/application.yml             |     0
 .../pom.xml                                        |     7 +-
 .../airavata/k8s/task/ingress/Application.java     |     0
 .../task/ingress/service/TaskExecutionService.java |    83 +
 .../src/main/resources/application.properties      |     0
 .../src/main/resources/application.yml             |     0
 .../k8s/task/egress/messaging/KafkaReceiver.java   |    45 -
 .../k8s/task/egress/messaging/KafkaSender.java     |    43 -
 .../k8s/task/egress/messaging/SenderConfig.java    |    69 -
 .../task/egress/service/TaskExecutionService.java  |   183 -
 .../src/main/resources/application.yml             |     4 -
 .../microservices/tasks/env-cleanup-task/pom.xml   |   156 -
 .../airavata/k8s/task/cleanup/Application.java     |    49 -
 .../k8s/task/cleanup/messaging/KafkaReceiver.java  |    45 -
 .../k8s/task/cleanup/messaging/KafkaSender.java    |    43 -
 .../k8s/task/cleanup/messaging/ReceiverConfig.java |    85 -
 .../k8s/task/cleanup/messaging/SenderConfig.java   |    69 -
 .../task/cleanup/service/TaskExecutionService.java |   153 -
 .../src/main/resources/application.properties      |     5 -
 .../src/main/resources/application.yml             |     4 -
 .../microservices/tasks/env-setup-task/pom.xml     |   157 -
 .../airavata/k8s/task/env/setup/Application.java   |    49 -
 .../task/env/setup/messaging/KafkaReceiver.java    |    45 -
 .../k8s/task/env/setup/messaging/KafkaSender.java  |    43 -
 .../task/env/setup/messaging/ReceiverConfig.java   |    85 -
 .../k8s/task/env/setup/messaging/SenderConfig.java |    69 -
 .../env/setup/service/TaskExecutionService.java    |   153 -
 .../src/main/resources/application.properties      |     5 -
 .../k8s/task/ingress/messaging/KafkaReceiver.java  |    45 -
 .../k8s/task/ingress/messaging/KafkaSender.java    |    43 -
 .../k8s/task/ingress/messaging/ReceiverConfig.java |    85 -
 .../k8s/task/ingress/messaging/SenderConfig.java   |    69 -
 .../task/ingress/service/TaskExecutionService.java |   126 -
 .../k8s/task/job/messaging/ReceiverConfig.java     |    85 -
 .../k8s/task/job/service/TaskExecutionService.java |   160 -
 .../service/ExperimentLaunchService.java           |    20 +-
 airavata-kubernetes/modules/task-api/pom.xml       |    54 +
 .../k8s/task/api/AbstractTaskExecutionService.java |   123 +
 .../apache/airavata/k8s/task/api/TaskContext.java  |    54 +
 .../k8s/task/api/TaskContextDeserializer.java      |    29 +
 .../k8s/task/api/TaskContextSerializer.java        |    28 +
 .../k8s/task/api}/messaging/KafkaReceiver.java     |    13 +-
 .../k8s/task/api}/messaging/KafkaSender.java       |     2 +-
 .../k8s/task/api}/messaging/ReceiverConfig.java    |     4 +-
 .../k8s/task/api}/messaging/SenderConfig.java      |     2 +-
 airavata-kubernetes/pom.xml                        |     9 +-
 .../web-console/src/app/app.module.ts              |     6 +-
 .../src/app/components/dashboard/dashboard.html    |     3 +
 .../app/components/dashboard/dashboard.routes.ts   |    10 +
 .../components/workflow/create/create.component.ts |   435 +
 .../src/app/components/workflow/create/create.html |    27 +
 .../src/app/components/workflow/list/list.html     |    23 +
 .../workflow/list/workflow.list.component.ts       |    49 +
 .../type/operation/operation.inport.type.model.ts  |     9 +
 .../type/operation/operation.outport.type.model.ts |     9 +
 .../task/type/operation/operation.type.model.ts    |    29 +
 .../app/models/task/type/task.input.type.model.ts  |    14 +
 .../src/app/models/task/type/task.outport.model.ts |    12 +
 .../app/models/task/type/task.output.type.model.ts |    12 +
 .../src/app/models/task/type/task.type.model.ts    |    39 +
 .../src/app/models/workflow/workflow.model.ts      |     8 +
 .../web-console/src/app/services/task.service.ts   |     4 +
 .../src/app/services/workflow.service.ts           |    17 +
 .../web-console/src/assets/css/common.css          |   160 +
 .../web-console/src/assets/css/explorer.css        |    18 +
 .../web-console/src/assets/icons/copy.png          |   Bin 0 -> 3575 bytes
 .../web-console/src/assets/icons/http.png          |   Bin 0 -> 3218 bytes
 .../web-console/src/assets/icons/parallel.png      |   Bin 0 -> 3150 bytes
 .../web-console/src/assets/icons/s3.png            |   Bin 0 -> 3988 bytes
 .../web-console/src/assets/icons/ssh.png           |   Bin 0 -> 3815 bytes
 .../web-console/src/assets/icons/start.png         |   Bin 0 -> 3303 bytes
 .../web-console/src/assets/icons/stop.png          |   Bin 0 -> 3525 bytes
 .../web-console/src/assets/images/button.gif       |   Bin 0 -> 137 bytes
 .../web-console/src/assets/images/close.gif        |   Bin 0 -> 70 bytes
 .../web-console/src/assets/images/collapsed.gif    |   Bin 0 -> 877 bytes
 .../web-console/src/assets/images/error.gif        |   Bin 0 -> 907 bytes
 .../web-console/src/assets/images/expanded.gif     |   Bin 0 -> 878 bytes
 .../web-console/src/assets/images/maximize.gif     |   Bin 0 -> 843 bytes
 .../web-console/src/assets/images/minimize.gif     |   Bin 0 -> 64 bytes
 .../web-console/src/assets/images/normalize.gif    |   Bin 0 -> 845 bytes
 .../web-console/src/assets/images/point.gif        |   Bin 0 -> 55 bytes
 .../web-console/src/assets/images/resize.gif       |   Bin 0 -> 74 bytes
 .../web-console/src/assets/images/separator.gif    |   Bin 0 -> 146 bytes
 .../web-console/src/assets/images/submenu.gif      |   Bin 0 -> 56 bytes
 .../web-console/src/assets/images/transparent.gif  |   Bin 0 -> 90 bytes
 .../web-console/src/assets/images/warning.gif      |   Bin 0 -> 276 bytes
 .../web-console/src/assets/images/warning.png      |   Bin 0 -> 425 bytes
 .../web-console/src/assets/images/window-title.gif |   Bin 0 -> 275 bytes
 .../web-console/src/assets/images/window.gif       |   Bin 0 -> 75 bytes
 .../web-console/src/assets/js/components.js        |    53 +
 .../src/assets/js/editor/mxDefaultKeyHandler.js    |   126 +
 .../src/assets/js/editor/mxDefaultPopupMenu.js     |   306 +
 .../src/assets/js/editor/mxDefaultToolbar.js       |   564 +
 .../web-console/src/assets/js/editor/mxEditor.js   |  3114 +++++
 .../src/assets/js/handler/mxCellHighlight.js       |   314 +
 .../src/assets/js/handler/mxCellMarker.js          |   430 +
 .../src/assets/js/handler/mxCellTracker.js         |   145 +
 .../src/assets/js/handler/mxConnectionHandler.js   |  2204 ++++
 .../src/assets/js/handler/mxConstraintHandler.js   |   520 +
 .../src/assets/js/handler/mxEdgeHandler.js         |  2409 ++++
 .../src/assets/js/handler/mxEdgeSegmentHandler.js  |   401 +
 .../src/assets/js/handler/mxElbowEdgeHandler.js    |   229 +
 .../src/assets/js/handler/mxGraphHandler.js        |  1074 ++
 .../web-console/src/assets/js/handler/mxHandle.js  |   353 +
 .../src/assets/js/handler/mxKeyHandler.js          |   428 +
 .../src/assets/js/handler/mxPanningHandler.js      |   462 +
 .../src/assets/js/handler/mxPopupMenuHandler.js    |   218 +
 .../src/assets/js/handler/mxRubberband.js          |   401 +
 .../assets/js/handler/mxSelectionCellsHandler.js   |   287 +
 .../src/assets/js/handler/mxTooltipHandler.js      |   337 +
 .../src/assets/js/handler/mxVertexHandler.js       |  1950 +++
 .../src => web-console/src/assets}/js/index.txt    |    96 +-
 .../web-console/src/assets/js/io/mxCellCodec.js    |   189 +
 .../src/assets/js/io/mxChildChangeCodec.js         |   149 +
 .../web-console/src/assets/js/io/mxCodec.js        |   596 +
 .../src/assets/js/io/mxCodecRegistry.js            |   137 +
 .../src/assets/js/io/mxDefaultKeyHandlerCodec.js   |    88 +
 .../src/assets/js/io/mxDefaultPopupMenuCodec.js    |    54 +
 .../src/assets/js/io/mxDefaultToolbarCodec.js      |   312 +
 .../web-console/src/assets/js/io/mxEditorCodec.js  |   245 +
 .../src/assets/js/io/mxGenericChangeCodec.js       |    64 +
 .../web-console/src/assets/js/io/mxGraphCodec.js   |    28 +
 .../src/assets/js/io/mxGraphViewCodec.js           |   197 +
 .../web-console/src/assets/js/io/mxModelCodec.js   |    80 +
 .../web-console/src/assets/js/io/mxObjectCodec.js  |  1077 ++
 .../src/assets/js/io/mxRootChangeCodec.js          |    83 +
 .../src/assets/js/io/mxStylesheetCodec.js          |   217 +
 .../src/assets/js/io/mxTerminalChangeCodec.js      |    42 +
 .../model/mxGraphAbstractHierarchyCell.js          |   206 +
 .../hierarchical/model/mxGraphHierarchyEdge.js     |   187 +
 .../hierarchical/model/mxGraphHierarchyModel.js    |   681 +
 .../hierarchical/model/mxGraphHierarchyNode.js     |   220 +
 .../layout/hierarchical/model/mxSwimlaneModel.js   |   801 ++
 .../js/layout/hierarchical/mxHierarchicalLayout.js |   846 ++
 .../js/layout/hierarchical/mxSwimlaneLayout.js     |   937 ++
 .../hierarchical/stage/mxCoordinateAssignment.js   |  1830 +++
 .../stage/mxHierarchicalLayoutStage.js             |    25 +
 .../stage/mxMedianHybridCrossingReduction.js       |   675 +
 .../hierarchical/stage/mxMinimumCycleRemover.js    |   108 +
 .../hierarchical/stage/mxSwimlaneOrdering.js       |    96 +
 .../src/assets/js/layout/mxCircleLayout.js         |   203 +
 .../src/assets/js/layout/mxCompactTreeLayout.js    |  1203 ++
 .../src/assets/js/layout/mxCompositeLayout.js      |   101 +
 .../src/assets/js/layout/mxEdgeLabelLayout.js      |   165 +
 .../src/assets/js/layout/mxFastOrganicLayout.js    |   591 +
 .../src/assets/js/layout/mxGraphLayout.js          |   461 +
 .../src/assets/js/layout/mxParallelEdgeLayout.js   |   225 +
 .../src/assets/js/layout/mxPartitionLayout.js      |   240 +
 .../src/assets/js/layout/mxRadialTreeLayout.js     |   317 +
 .../src/assets/js/layout/mxStackLayout.js          |   515 +
 .../web-console/src/assets/js/model/mxCell.js      |   825 ++
 .../web-console/src/assets/js/model/mxCellPath.js  |   163 +
 .../web-console/src/assets/js/model/mxGeometry.js  |   415 +
 .../src/assets/js/model/mxGraphModel.js            |  2667 ++++
 .../web-console/src/assets/js/mxClient.js          |   769 ++
 .../web-console/src/assets/js/shape/mxActor.js     |    86 +
 .../web-console/src/assets/js/shape/mxArrow.js     |   115 +
 .../src/assets/js/shape/mxArrowConnector.js        |   485 +
 .../web-console/src/assets/js/shape/mxCloud.js     |    55 +
 .../web-console/src/assets/js/shape/mxConnector.js |   149 +
 .../web-console/src/assets/js/shape/mxCylinder.js  |   105 +
 .../src/assets/js/shape/mxDoubleEllipse.js         |   114 +
 .../web-console/src/assets/js/shape/mxEllipse.js   |    48 +
 .../web-console/src/assets/js/shape/mxHexagon.js   |    34 +
 .../src/assets/js/shape/mxImageShape.js            |   233 +
 .../web-console/src/assets/js/shape/mxLabel.js     |   275 +
 .../web-console/src/assets/js/shape/mxLine.js      |    51 +
 .../web-console/src/assets/js/shape/mxMarker.js    |   208 +
 .../web-console/src/assets/js/shape/mxPolyline.js  |   127 +
 .../src/assets/js/shape/mxRectangleShape.js        |   117 +
 .../web-console/src/assets/js/shape/mxRhombus.js   |    54 +
 .../web-console/src/assets/js/shape/mxShape.js     |  1604 +++
 .../web-console/src/assets/js/shape/mxStencil.js   |   761 ++
 .../src/assets/js/shape/mxStencilRegistry.js       |    53 +
 .../web-console/src/assets/js/shape/mxSwimlane.js  |   410 +
 .../web-console/src/assets/js/shape/mxText.js      |  1263 ++
 .../web-console/src/assets/js/shape/mxTriangle.js  |    33 +
 .../src/assets/js/util/mxAbstractCanvas2D.js       |   642 +
 .../web-console/src/assets/js/util/mxAnimation.js  |    92 +
 .../src/assets/js/util/mxAutoSaveManager.js        |   213 +
 .../web-console/src/assets/js/util/mxClipboard.js  |   221 +
 .../web-console/src/assets/js/util/mxConstants.js  |  2275 ++++
 .../web-console/src/assets/js/util/mxDictionary.js |   130 +
 .../web-console/src/assets/js/util/mxDivResizer.js |   151 +
 .../web-console/src/assets/js/util/mxDragSource.js |   679 +
 .../web-console/src/assets/js/util/mxEffects.js    |   211 +
 .../web-console/src/assets/js/util/mxEvent.js      |  1406 ++
 .../src/assets/js/util/mxEventObject.js            |   111 +
 .../src/assets/js/util/mxEventSource.js            |   189 +
 .../web-console/src/assets/js/util/mxForm.js       |   202 +
 .../web-console/src/assets/js/util/mxGuide.js      |   401 +
 .../web-console/src/assets/js/util/mxImage.js      |    40 +
 .../src/assets/js/util/mxImageBundle.js            |   103 +
 .../src/assets/js/util/mxImageExport.js            |   175 +
 .../web-console/src/assets/js/util/mxLog.js        |   413 +
 .../web-console/src/assets/js/util/mxMorphing.js   |   248 +
 .../web-console/src/assets/js/util/mxMouseEvent.js |   244 +
 .../src/assets/js/util/mxObjectIdentity.js         |    72 +
 .../src/assets/js/util/mxPanningManager.js         |   262 +
 .../web-console/src/assets/js/util/mxPoint.js      |    54 +
 .../web-console/src/assets/js/util/mxPopupMenu.js  |   613 +
 .../web-console/src/assets/js/util/mxRectangle.js  |   179 +
 .../web-console/src/assets/js/util/mxResources.js  |   450 +
 .../src/assets/js/util/mxSvgCanvas2D.js            |  2179 ++++
 .../web-console/src/assets/js/util/mxToolbar.js    |   527 +
 .../src/assets/js/util/mxUndoManager.js            |   229 +
 .../src/assets/js/util/mxUndoableEdit.js           |   213 +
 .../src/assets/js/util/mxUrlConverter.js           |   151 +
 .../web-console/src/assets/js/util/mxUtils.js      |  4353 +++++++
 .../src/assets/js/util/mxVmlCanvas2D.js            |  1102 ++
 .../web-console/src/assets/js/util/mxWindow.js     |  1130 ++
 .../src/assets/js/util/mxXmlCanvas2D.js            |  1217 ++
 .../web-console/src/assets/js/util/mxXmlRequest.js |   463 +
 .../web-console/src/assets/js/view/mxCellEditor.js |  1069 ++
 .../src/assets/js/view/mxCellOverlay.js            |   233 +
 .../src/assets/js/view/mxCellRenderer.js           |  1553 +++
 .../web-console/src/assets/js/view/mxCellState.js  |   431 +
 .../src/assets/js/view/mxCellStatePreview.js       |   203 +
 .../src/assets/js/view/mxConnectionConstraint.js   |    50 +
 .../web-console/src/assets/js/view/mxEdgeStyle.js  |  1569 +++
 .../web-console/src/assets/js/view/mxGraph.js      | 12768 +++++++++++++++++++
 .../src/assets/js/view/mxGraphSelectionModel.js    |   436 +
 .../web-console/src/assets/js/view/mxGraphView.js  |  3001 +++++
 .../src/assets/js/view/mxLayoutManager.js          |   409 +
 .../src/assets/js/view/mxMultiplicity.js           |   257 +
 .../web-console/src/assets/js/view/mxOutline.js    |   761 ++
 .../web-console/src/assets/js/view/mxPerimeter.js  |   921 ++
 .../src/assets/js/view/mxPrintPreview.js           |  1175 ++
 .../src/assets/js/view/mxStyleRegistry.js          |    71 +
 .../web-console/src/assets/js/view/mxStylesheet.js |   266 +
 .../src/assets/js/view/mxSwimlaneManager.js        |   450 +
 .../src/assets/js/view/mxTemporaryCellStates.js    |   108 +
 .../web-console/src/assets/resources/editor.txt    |     5 +
 .../web-console/src/assets/resources/editor_de.txt |     5 +
 .../web-console/src/assets/resources/editor_zh.txt |     5 +
 .../web-console/src/assets/resources/graph.txt     |    11 +
 .../web-console/src/assets/resources/graph_de.txt  |    11 +
 .../web-console/src/assets/resources/graph_zh.txt  |    11 +
 airavata-kubernetes/web-console/src/index.html     |     6 +
 airavata-kubernetes/workflow-composer/index.html   |     2 +-
 .../workflow-composer/src/js/index.txt             |     6 +-
 316 files changed, 92931 insertions(+), 2525 deletions(-)

diff --git a/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/process/ProcessResource.java b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/process/ProcessResource.java
index 8805e74..b5081cf 100644
--- a/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/process/ProcessResource.java
+++ b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/process/ProcessResource.java
@@ -33,7 +33,9 @@ import java.util.List;
 public class ProcessResource {
 
     private long id;
+    private String name;
     private long experimentId;
+    private long workflowId;
     private long creationTime;
     private long lastUpdateTime;
     private List<ProcessStatusResource> processStatuses = new ArrayList<>();
@@ -41,6 +43,7 @@ public class ProcessResource {
     private List<Long> processErrorIds = new ArrayList<>();
     private String taskDag;
     private String experimentDataDir;
+    private String processType;
 
     public long getId() {
         return id;
@@ -122,4 +125,31 @@ public class ProcessResource {
         this.experimentDataDir = experimentDataDir;
         return this;
     }
+
+    public String getProcessType() {
+        return processType;
+    }
+
+    public ProcessResource setProcessType(String processType) {
+        this.processType = processType;
+        return this;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public ProcessResource setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public long getWorkflowId() {
+        return workflowId;
+    }
+
+    public ProcessResource setWorkflowId(long workflowId) {
+        this.workflowId = workflowId;
+        return this;
+    }
 }
diff --git a/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/TaskInputResource.java b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/TaskInputResource.java
new file mode 100644
index 0000000..562f416
--- /dev/null
+++ b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/TaskInputResource.java
@@ -0,0 +1,61 @@
+package org.apache.airavata.k8s.api.resources.task;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public class TaskInputResource {
+
+    private long id;
+    private String name;
+    private String value;
+    private String type;
+    private String importFrom;
+
+    public long getId() {
+        return id;
+    }
+
+    public TaskInputResource setId(long id) {
+        this.id = id;
+        return this;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public TaskInputResource setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public TaskInputResource setValue(String value) {
+        this.value = value;
+        return this;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public TaskInputResource setType(String type) {
+        this.type = type;
+        return this;
+    }
+
+    public String getImportFrom() {
+        return importFrom;
+    }
+
+    public TaskInputResource setImportFrom(String importFrom) {
+        this.importFrom = importFrom;
+        return this;
+    }
+}
diff --git a/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/TaskOutPortResource.java b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/TaskOutPortResource.java
new file mode 100644
index 0000000..de4ef1b
--- /dev/null
+++ b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/TaskOutPortResource.java
@@ -0,0 +1,40 @@
+package org.apache.airavata.k8s.api.resources.task;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public class TaskOutPortResource {
+    private long id;
+    private String name;
+    private int referenceId = 0;
+
+    public long getId() {
+        return id;
+    }
+
+    public TaskOutPortResource setId(long id) {
+        this.id = id;
+        return this;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public TaskOutPortResource setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public int getReferenceId() {
+        return referenceId;
+    }
+
+    public TaskOutPortResource setReferenceId(int referenceId) {
+        this.referenceId = referenceId;
+        return this;
+    }
+}
diff --git a/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/TaskOutputResource.java b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/TaskOutputResource.java
new file mode 100644
index 0000000..aa677d8
--- /dev/null
+++ b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/TaskOutputResource.java
@@ -0,0 +1,61 @@
+package org.apache.airavata.k8s.api.resources.task;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public class TaskOutputResource {
+
+    private long id;
+    private String name;
+    private String value;
+    private String type;
+    private String exportTo;
+
+    public long getId() {
+        return id;
+    }
+
+    public TaskOutputResource setId(long id) {
+        this.id = id;
+        return this;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public TaskOutputResource setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public TaskOutputResource setValue(String value) {
+        this.value = value;
+        return this;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public TaskOutputResource setType(String type) {
+        this.type = type;
+        return this;
+    }
+
+    public String getExportTo() {
+        return exportTo;
+    }
+
+    public TaskOutputResource setExportTo(String exportTo) {
+        this.exportTo = exportTo;
+        return this;
+    }
+}
diff --git a/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/TaskResource.java b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/TaskResource.java
index 10948f7..bdc34d7 100644
--- a/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/TaskResource.java
+++ b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/TaskResource.java
@@ -31,7 +31,8 @@ import java.util.List;
 public class TaskResource {
 
     private long id;
-    private int taskType;
+    private String name;
+    private long taskTypeId;
     private String taskTypeStr;
     private long parentProcessId;
     private long creationTime;
@@ -39,9 +40,13 @@ public class TaskResource {
     private List<TaskStatusResource> taskStatus = new ArrayList<>();
     private String taskDetail;
     private List<Long> taskErrorIds = new ArrayList<>();
-    private List<TaskParamResource> taskParams = new ArrayList<>();
+    private List<TaskInputResource> inputs = new ArrayList<>();
+    private List<TaskOutputResource> outputs = new ArrayList<>();
+    private List<TaskOutPortResource> outPorts = new ArrayList<>();
     private List<Long> jobIds;
     private int order;
+    private boolean startingTask;
+    private boolean stoppingTask;
 
     public long getId() {
         return id;
@@ -52,12 +57,12 @@ public class TaskResource {
         return this;
     }
 
-    public int getTaskType() {
-        return taskType;
+    public long getTaskTypeId() {
+        return taskTypeId;
     }
 
-    public TaskResource setTaskType(int taskType) {
-        this.taskType = taskType;
+    public TaskResource setTaskTypeId(long taskTypeId) {
+        this.taskTypeId = taskTypeId;
         return this;
     }
 
@@ -124,12 +129,21 @@ public class TaskResource {
         return this;
     }
 
-    public List<TaskParamResource> getTaskParams() {
-        return taskParams;
+    public List<TaskInputResource> getInputs() {
+        return inputs;
     }
 
-    public TaskResource setTaskParams(List<TaskParamResource> taskParams) {
-        this.taskParams = taskParams;
+    public TaskResource setInputs(List<TaskInputResource> inputs) {
+        this.inputs = inputs;
+        return this;
+    }
+
+    public List<TaskOutputResource> getOutputs() {
+        return outputs;
+    }
+
+    public TaskResource setOutputs(List<TaskOutputResource> outputs) {
+        this.outputs = outputs;
         return this;
     }
 
@@ -151,6 +165,40 @@ public class TaskResource {
         return this;
     }
 
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public List<TaskOutPortResource> getOutPorts() {
+        return outPorts;
+    }
+
+    public void setOutPorts(List<TaskOutPortResource> outPorts) {
+        this.outPorts = outPorts;
+    }
+
+    public boolean isStartingTask() {
+        return startingTask;
+    }
+
+    public TaskResource setStartingTask(boolean startingTask) {
+        this.startingTask = startingTask;
+        return this;
+    }
+
+    public boolean isStoppingTask() {
+        return stoppingTask;
+    }
+
+    public TaskResource setStoppingTask(boolean stoppingTask) {
+        this.stoppingTask = stoppingTask;
+        return this;
+    }
+
     public static final class TaskTypes {
         public static final int ENV_SETUP = 0;
         public static final int INGRESS_DATA_STAGING = 1;
diff --git a/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/type/TaskInputTypeResource.java b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/type/TaskInputTypeResource.java
new file mode 100644
index 0000000..bfab1bf
--- /dev/null
+++ b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/type/TaskInputTypeResource.java
@@ -0,0 +1,51 @@
+package org.apache.airavata.k8s.api.resources.task.type;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public class TaskInputTypeResource {
+
+    private long id;
+    private String name;
+    private String type;
+    private String defaultValue;
+
+    public long getId() {
+        return id;
+    }
+
+    public TaskInputTypeResource setId(long id) {
+        this.id = id;
+        return this;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public TaskInputTypeResource setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public TaskInputTypeResource setType(String type) {
+        this.type = type;
+        return this;
+    }
+
+    public String getDefaultValue() {
+        return defaultValue;
+    }
+
+    public TaskInputTypeResource setDefaultValue(String defaultValue) {
+        this.defaultValue = defaultValue;
+        return this;
+    }
+}
diff --git a/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/type/TaskOutPortTypeResource.java b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/type/TaskOutPortTypeResource.java
new file mode 100644
index 0000000..ad274ca
--- /dev/null
+++ b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/type/TaskOutPortTypeResource.java
@@ -0,0 +1,41 @@
+package org.apache.airavata.k8s.api.resources.task.type;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public class TaskOutPortTypeResource {
+
+    private long id;
+    private String name;
+    private int order = 0;
+
+    public long getId() {
+        return id;
+    }
+
+    public TaskOutPortTypeResource setId(long id) {
+        this.id = id;
+        return this;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public TaskOutPortTypeResource setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public int getOrder() {
+        return order;
+    }
+
+    public TaskOutPortTypeResource setOrder(int order) {
+        this.order = order;
+        return this;
+    }
+}
diff --git a/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/type/TaskOutputTypeResource.java b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/type/TaskOutputTypeResource.java
new file mode 100644
index 0000000..f4fd618
--- /dev/null
+++ b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/type/TaskOutputTypeResource.java
@@ -0,0 +1,41 @@
+package org.apache.airavata.k8s.api.resources.task.type;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public class TaskOutputTypeResource {
+
+    private long id;
+    private String name;
+    private String type;
+
+    public long getId() {
+        return id;
+    }
+
+    public TaskOutputTypeResource setId(long id) {
+        this.id = id;
+        return this;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public TaskOutputTypeResource setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public TaskOutputTypeResource setType(String type) {
+        this.type = type;
+        return this;
+    }
+}
diff --git a/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/type/TaskTypeResource.java b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/type/TaskTypeResource.java
new file mode 100644
index 0000000..1818b47
--- /dev/null
+++ b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/task/type/TaskTypeResource.java
@@ -0,0 +1,84 @@
+package org.apache.airavata.k8s.api.resources.task.type;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public class TaskTypeResource {
+
+    private long id;
+    private String name;
+    private String topicName;
+    private String icon;
+    private List<TaskInputTypeResource> inputTypes = new ArrayList<>();
+    private List<TaskOutputTypeResource> outputTypes = new ArrayList<>();
+    private List<TaskOutPortTypeResource> outPorts = new ArrayList<>();
+
+    public long getId() {
+        return id;
+    }
+
+    public TaskTypeResource setId(long id) {
+        this.id = id;
+        return this;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public TaskTypeResource setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public String getTopicName() {
+        return topicName;
+    }
+
+    public TaskTypeResource setTopicName(String topicName) {
+        this.topicName = topicName;
+        return this;
+    }
+
+    public List<TaskInputTypeResource> getInputTypes() {
+        return inputTypes;
+    }
+
+    public TaskTypeResource setInputTypes(List<TaskInputTypeResource> inputTypes) {
+        this.inputTypes = inputTypes;
+        return this;
+    }
+
+    public List<TaskOutputTypeResource> getOutputTypes() {
+        return outputTypes;
+    }
+
+    public TaskTypeResource setOutputTypes(List<TaskOutputTypeResource> outputTypes) {
+        this.outputTypes = outputTypes;
+        return this;
+    }
+
+    public List<TaskOutPortTypeResource> getOutPorts() {
+        return outPorts;
+    }
+
+    public TaskTypeResource setOutPorts(List<TaskOutPortTypeResource> outPorts) {
+        this.outPorts = outPorts;
+        return this;
+    }
+
+    public String getIcon() {
+        return icon;
+    }
+
+    public TaskTypeResource setIcon(String icon) {
+        this.icon = icon;
+        return this;
+    }
+}
diff --git a/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/workflow/WorkflowResource.java b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/workflow/WorkflowResource.java
new file mode 100644
index 0000000..5631e50
--- /dev/null
+++ b/airavata-kubernetes/modules/api-resource/src/main/java/org/apache/airavata/k8s/api/resources/workflow/WorkflowResource.java
@@ -0,0 +1,54 @@
+package org.apache.airavata.k8s.api.resources.workflow;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public class WorkflowResource {
+
+    private long id;
+    private String name;
+    private String workflowGraphXML;
+    private List<Long> processIds = new ArrayList<>();
+
+    public long getId() {
+        return id;
+    }
+
+    public WorkflowResource setId(long id) {
+        this.id = id;
+        return this;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public WorkflowResource setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public String getWorkflowGraphXML() {
+        return workflowGraphXML;
+    }
+
+    public WorkflowResource setWorkflowGraphXML(String workflowGraphXML) {
+        this.workflowGraphXML = workflowGraphXML;
+        return this;
+    }
+
+    public List<Long> getProcessIds() {
+        return processIds;
+    }
+
+    public WorkflowResource setProcessIds(List<Long> processIds) {
+        this.processIds = processIds;
+        return this;
+    }
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/controller/TaskController.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/controller/TaskController.java
index ef8c797..2fb2334 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/controller/TaskController.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/controller/TaskController.java
@@ -22,7 +22,7 @@ package org.apache.airavata.k8s.api.server.controller;
 import org.apache.airavata.k8s.api.resources.task.TaskResource;
 import org.apache.airavata.k8s.api.resources.task.TaskStatusResource;
 import org.apache.airavata.k8s.api.server.ServerRuntimeException;
-import org.apache.airavata.k8s.api.server.service.TaskService;
+import org.apache.airavata.k8s.api.server.service.task.TaskService;
 import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.*;
 
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/controller/TaskTypeController.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/controller/TaskTypeController.java
new file mode 100644
index 0000000..5424b72
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/controller/TaskTypeController.java
@@ -0,0 +1,33 @@
+package org.apache.airavata.k8s.api.server.controller;
+
+import org.apache.airavata.k8s.api.resources.task.type.TaskTypeResource;
+import org.apache.airavata.k8s.api.server.service.task.type.TaskTypeService;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+@RestController
+@RequestMapping(path="/taskType")
+public class TaskTypeController {
+
+    @Resource
+    private TaskTypeService taskTypeService;
+
+    @GetMapping(path = "", produces = MediaType.APPLICATION_JSON_VALUE)
+    public List<TaskTypeResource> getAll() {
+        return taskTypeService.getAll();
+    }
+
+    @PostMapping( path = "", consumes = MediaType.APPLICATION_JSON_VALUE)
+    public long createTask(@RequestBody TaskTypeResource resource) {
+        return taskTypeService.create(resource);
+    }
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/controller/WorkflowController.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/controller/WorkflowController.java
new file mode 100644
index 0000000..136f839
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/controller/WorkflowController.java
@@ -0,0 +1,39 @@
+package org.apache.airavata.k8s.api.server.controller;
+
+import org.apache.airavata.k8s.api.resources.experiment.ExperimentResource;
+import org.apache.airavata.k8s.api.resources.workflow.WorkflowResource;
+import org.apache.airavata.k8s.api.server.service.WorkflowService;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+@RestController
+@RequestMapping(path="/workflow")
+public class WorkflowController {
+
+    @Resource
+    private WorkflowService workflowService;
+
+    @PostMapping(path = "create/{name}")
+    public long createWorkflow(@PathVariable("name") String name, @RequestBody String workflowGraph) {
+        return this.workflowService.createWorkflow(new WorkflowResource().setName(name).setWorkflowGraphXML(workflowGraph));
+    }
+
+    @GetMapping(path = "", produces = MediaType.APPLICATION_JSON_VALUE)
+    public List<WorkflowResource> getAllWorkflows() {
+        return this.workflowService.getAll();
+    }
+
+    @GetMapping(path = "{id}/launch", produces = MediaType.APPLICATION_JSON_VALUE)
+    public long launchExperiment(@PathVariable("id") long id) {
+        return this.workflowService.launchWorkflow(id);
+    }
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/process/ProcessModel.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/process/ProcessModel.java
index d3f8984..5a4054f 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/process/ProcessModel.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/process/ProcessModel.java
@@ -22,6 +22,7 @@ package org.apache.airavata.k8s.api.server.model.process;
 import org.apache.airavata.k8s.api.server.model.commons.ErrorModel;
 import org.apache.airavata.k8s.api.server.model.experiment.Experiment;
 import org.apache.airavata.k8s.api.server.model.task.TaskModel;
+import org.apache.airavata.k8s.api.server.model.workflow.Workflow;
 
 import javax.persistence.*;
 import java.util.ArrayList;
@@ -45,6 +46,11 @@ public class ProcessModel {
     @ManyToOne
     private Experiment experiment;
 
+    @ManyToOne
+    private Workflow workflow;
+
+    private String name;
+
     private long creationTime;
     private long lastUpdateTime;
 
@@ -61,75 +67,117 @@ public class ProcessModel {
 
     private String experimentDataDir;
 
+    private ProcessType processType = ProcessType.EXPERIMENT;
+
     public long getId() {
         return id;
     }
 
-    public void setId(long id) {
+    public ProcessModel setId(long id) {
         this.id = id;
+        return this;
     }
 
     public Experiment getExperiment() {
         return experiment;
     }
 
-    public void setExperiment(Experiment experiment) {
+    public ProcessModel setExperiment(Experiment experiment) {
         this.experiment = experiment;
+        return this;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public ProcessModel setName(String name) {
+        this.name = name;
+        return this;
     }
 
     public long getCreationTime() {
         return creationTime;
     }
 
-    public void setCreationTime(long creationTime) {
+    public ProcessModel setCreationTime(long creationTime) {
         this.creationTime = creationTime;
+        return this;
     }
 
     public long getLastUpdateTime() {
         return lastUpdateTime;
     }
 
-    public void setLastUpdateTime(long lastUpdateTime) {
+    public ProcessModel setLastUpdateTime(long lastUpdateTime) {
         this.lastUpdateTime = lastUpdateTime;
+        return this;
     }
 
     public List<ProcessStatus> getProcessStatuses() {
         return processStatuses;
     }
 
-    public void setProcessStatuses(List<ProcessStatus> processStatuses) {
+    public ProcessModel setProcessStatuses(List<ProcessStatus> processStatuses) {
         this.processStatuses = processStatuses;
+        return this;
     }
 
     public List<TaskModel> getTasks() {
         return tasks;
     }
 
-    public void setTasks(List<TaskModel> tasks) {
+    public ProcessModel setTasks(List<TaskModel> tasks) {
         this.tasks = tasks;
+        return this;
     }
 
     public String getTaskDag() {
         return taskDag;
     }
 
-    public void setTaskDag(String taskDag) {
+    public ProcessModel setTaskDag(String taskDag) {
         this.taskDag = taskDag;
+        return this;
     }
 
     public List<ErrorModel> getProcessErrors() {
         return processErrors;
     }
 
-    public void setProcessErrors(List<ErrorModel> processErrors) {
+    public ProcessModel setProcessErrors(List<ErrorModel> processErrors) {
         this.processErrors = processErrors;
+        return this;
     }
 
     public String getExperimentDataDir() {
         return experimentDataDir;
     }
 
-    public void setExperimentDataDir(String experimentDataDir) {
+    public ProcessModel setExperimentDataDir(String experimentDataDir) {
         this.experimentDataDir = experimentDataDir;
+        return this;
+    }
+
+    public ProcessType getProcessType() {
+        return processType;
+    }
+
+    public ProcessModel setProcessType(ProcessType processType) {
+        this.processType = processType;
+        return this;
+    }
+
+    public Workflow getWorkflow() {
+        return workflow;
+    }
+
+    public ProcessModel setWorkflow(Workflow workflow) {
+        this.workflow = workflow;
+        return this;
+    }
+
+    public enum ProcessType {
+        WORKFLOW, EXPERIMENT;
     }
 }
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/TaskDAG.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/TaskDAG.java
new file mode 100644
index 0000000..88ffef3
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/TaskDAG.java
@@ -0,0 +1,52 @@
+package org.apache.airavata.k8s.api.server.model.task;
+
+import com.sun.javafx.tk.Toolkit;
+
+import javax.persistence.*;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+@Entity
+public class TaskDAG {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    private long id;
+
+    @ManyToOne
+    private TaskOutPort sourceOutPort;
+
+    @ManyToOne
+    private TaskModel targetTask;
+
+    public long getId() {
+        return id;
+    }
+
+    public TaskDAG setId(long id) {
+        this.id = id;
+        return this;
+    }
+
+    public TaskOutPort getSourceOutPort() {
+        return sourceOutPort;
+    }
+
+    public TaskDAG setSourceOutPort(TaskOutPort sourceOutPort) {
+        this.sourceOutPort = sourceOutPort;
+        return this;
+    }
+
+    public TaskModel getTargetTask() {
+        return targetTask;
+    }
+
+    public TaskDAG setTargetTask(TaskModel targetTask) {
+        this.targetTask = targetTask;
+        return this;
+    }
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/TaskInput.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/TaskInput.java
new file mode 100644
index 0000000..4a2d703
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/TaskInput.java
@@ -0,0 +1,86 @@
+package org.apache.airavata.k8s.api.server.model.task;
+
+import javax.persistence.*;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+@Entity
+public class TaskInput {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    private long id;
+
+    @Column(name = "TASK_INPUT_NAME")
+    private String name;
+
+    @Column(name = "TASK_INPUT_VALUE")
+    private String value;
+
+    @Column(name = "TASK_INPUT_TYPE")
+    private String type;
+
+    @Column(name = "IMPORT_FORM")
+    private String importFrom;
+
+    @ManyToOne
+    private TaskModel taskModel;
+
+    public long getId() {
+        return id;
+    }
+
+    public TaskInput setId(long id) {
+        this.id = id;
+        return this;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public TaskInput setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public TaskInput setValue(String value) {
+        this.value = value;
+        return this;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public TaskInput setType(String type) {
+        this.type = type;
+        return this;
+    }
+
+    public TaskModel getTaskModel() {
+        return taskModel;
+    }
+
+    public TaskInput setTaskModel(TaskModel taskModel) {
+        this.taskModel = taskModel;
+        return this;
+    }
+
+    public String getImportFrom() {
+        return importFrom;
+    }
+
+    public TaskInput setImportFrom(String importFrom) {
+        this.importFrom = importFrom;
+        return this;
+    }
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/TaskModel.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/TaskModel.java
index 6ce995d..247bae1 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/TaskModel.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/TaskModel.java
@@ -22,12 +22,11 @@ package org.apache.airavata.k8s.api.server.model.task;
 import org.apache.airavata.k8s.api.server.model.commons.ErrorModel;
 import org.apache.airavata.k8s.api.server.model.job.JobModel;
 import org.apache.airavata.k8s.api.server.model.process.ProcessModel;
+import org.apache.airavata.k8s.api.server.model.task.type.TaskModelType;
 
 import javax.persistence.*;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 /**
  * TODO: Class level comments please
@@ -43,7 +42,10 @@ public class TaskModel {
     @GeneratedValue(strategy = GenerationType.AUTO)
     private long id;
 
-    private TaskTypes taskType;
+    private String name;
+
+    @ManyToOne
+    private TaskModelType taskType;
 
     @ManyToOne
     private ProcessModel parentProcess;
@@ -52,6 +54,10 @@ public class TaskModel {
     private long lastUpdateTime;
     private int orderIndex;
 
+    private boolean startingTask;
+
+    private boolean stoppingTask;
+
     @OneToMany(mappedBy = "taskModel", cascade = CascadeType.ALL)
     private List<TaskStatus> taskStatuses = new ArrayList<>();
 
@@ -64,7 +70,13 @@ public class TaskModel {
     private List<JobModel> jobs = new ArrayList<>();
 
     @OneToMany(mappedBy = "taskModel", cascade = CascadeType.ALL)
-    private List<TaskParam> taskParams = new ArrayList<>();
+    private List<TaskInput> taskInputs = new ArrayList<>();
+
+    @OneToMany(mappedBy = "taskModel", cascade = CascadeType.ALL)
+    private List<TaskOutput> taskOutputs = new ArrayList<>();
+
+    @OneToMany(mappedBy = "taskModel", cascade = CascadeType.ALL)
+    private List<TaskOutPort> taskOutPorts = new ArrayList<>();
 
     public long getId() {
         return id;
@@ -74,14 +86,6 @@ public class TaskModel {
         this.id = id;
     }
 
-    public TaskTypes getTaskType() {
-        return taskType;
-    }
-
-    public void setTaskType(TaskTypes taskType) {
-        this.taskType = taskType;
-    }
-
     public ProcessModel getParentProcess() {
         return parentProcess;
     }
@@ -138,12 +142,48 @@ public class TaskModel {
         this.jobs = jobs;
     }
 
-    public List<TaskParam> getTaskParams() {
-        return taskParams;
+    public List<TaskInput> getTaskInputs() {
+        return taskInputs;
+    }
+
+    public TaskModel setTaskInputs(List<TaskInput> taskInputs) {
+        this.taskInputs = taskInputs;
+        return this;
+    }
+
+    public List<TaskOutput> getTaskOutputs() {
+        return taskOutputs;
+    }
+
+    public TaskModel setTaskOutputs(List<TaskOutput> taskOutputs) {
+        this.taskOutputs = taskOutputs;
+        return this;
+    }
+
+    public TaskModelType getTaskType() {
+        return taskType;
     }
 
-    public TaskModel setTaskParams(List<TaskParam> taskParams) {
-        this.taskParams = taskParams;
+    public TaskModel setTaskType(TaskModelType taskType) {
+        this.taskType = taskType;
+        return this;
+    }
+
+    public boolean isStartingTask() {
+        return startingTask;
+    }
+
+    public TaskModel setStartingTask(boolean startingTask) {
+        this.startingTask = startingTask;
+        return this;
+    }
+
+    public boolean isStoppingTask() {
+        return stoppingTask;
+    }
+
+    public TaskModel setStoppingTask(boolean stoppingTask) {
+        this.stoppingTask = stoppingTask;
         return this;
     }
 
@@ -156,35 +196,19 @@ public class TaskModel {
         return this;
     }
 
-    public enum TaskTypes {
-        ENV_SETUP(0),
-        INGRESS_DATA_STAGING(1),
-        EGRESS_DATA_STAGING(2),
-        JOB_SUBMISSION(3),
-        ENV_CLEANUP(4),
-        MONITORING(5),
-        OUTPUT_FETCHING(6);
-
-        private static Map<Integer, TaskTypes> map = new HashMap<>();
-
-        static {
-            for (TaskTypes taskType : TaskTypes.values()) {
-                map.put(taskType.value, taskType);
-            }
-        }
-        private final int value;
-
-        public static TaskTypes valueOf(int taskType) {
-            return map.get(taskType);
-        }
+    public String getName() {
+        return name;
+    }
 
-        private TaskTypes(int value) {
-            this.value = value;
-        }
+    public void setName(String name) {
+        this.name = name;
+    }
 
-        public int getValue() {
-            return value;
-        }
+    public List<TaskOutPort> getTaskOutPorts() {
+        return taskOutPorts;
     }
 
+    public void setTaskOutPorts(List<TaskOutPort> taskOutPorts) {
+        this.taskOutPorts = taskOutPorts;
+    }
 }
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/TaskOutPort.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/TaskOutPort.java
new file mode 100644
index 0000000..2f86638
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/TaskOutPort.java
@@ -0,0 +1,57 @@
+package org.apache.airavata.k8s.api.server.model.task;
+
+import javax.persistence.*;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+@Entity
+public class TaskOutPort {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    private long id;
+
+    private String name;
+
+    private int referenceId;
+
+    @ManyToOne
+    private TaskModel taskModel;
+
+    public long getId() {
+        return id;
+    }
+
+    public void setId(long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public int getReferenceId() {
+        return referenceId;
+    }
+
+    public TaskOutPort setReferenceId(int referenceId) {
+        this.referenceId = referenceId;
+        return this;
+    }
+
+    public TaskModel getTaskModel() {
+        return taskModel;
+    }
+
+    public void setTaskModel(TaskModel taskModel) {
+        this.taskModel = taskModel;
+    }
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/TaskOutput.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/TaskOutput.java
new file mode 100644
index 0000000..4265683
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/TaskOutput.java
@@ -0,0 +1,86 @@
+package org.apache.airavata.k8s.api.server.model.task;
+
+import javax.persistence.*;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+@Entity
+public class TaskOutput {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    private long id;
+
+    @Column(name = "TASK_OUTPUT_NAME")
+    private String name;
+
+    @Column(name = "TASK_OUTPUT_VALUE")
+    private String value;
+
+    @Column(name = "TASK_OUTPUT_TYPE")
+    private String type;
+
+    @Column(name = "EXPORT_TO")
+    private String exportTo;
+
+    @ManyToOne
+    private TaskModel taskModel;
+
+    public long getId() {
+        return id;
+    }
+
+    public TaskOutput setId(long id) {
+        this.id = id;
+        return this;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public TaskOutput setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public TaskOutput setValue(String value) {
+        this.value = value;
+        return this;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public TaskOutput setType(String type) {
+        this.type = type;
+        return this;
+    }
+
+    public String getExportTo() {
+        return exportTo;
+    }
+
+    public TaskOutput setExportTo(String exportTo) {
+        this.exportTo = exportTo;
+        return this;
+    }
+
+    public TaskModel getTaskModel() {
+        return taskModel;
+    }
+
+    public TaskOutput setTaskModel(TaskModel taskModel) {
+        this.taskModel = taskModel;
+        return this;
+    }
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/TaskParam.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/TaskParam.java
deleted file mode 100644
index cbeaace..0000000
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/TaskParam.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/**
- *
- * 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.airavata.k8s.api.server.model.task;
-
-import javax.persistence.*;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-@Entity
-@Table(name = "TASK_PARAM")
-public class TaskParam {
-
-    @Id
-    @GeneratedValue(strategy = GenerationType.AUTO)
-    private long id;
-
-    @Column(name = "PARAM_KEY")
-    private String key;
-
-    @Column(name = "PARAM_VALUE")
-    private String value;
-
-    @ManyToOne
-    private TaskModel taskModel;
-
-    public long getId() {
-        return id;
-    }
-
-    public TaskParam setId(long id) {
-        this.id = id;
-        return this;
-    }
-
-    public String getKey() {
-        return key;
-    }
-
-    public TaskParam setKey(String key) {
-        this.key = key;
-        return this;
-    }
-
-    public String getValue() {
-        return value;
-    }
-
-    public TaskParam setValue(String value) {
-        this.value = value;
-        return this;
-    }
-
-    public TaskModel getTaskModel() {
-        return taskModel;
-    }
-
-    public TaskParam setTaskModel(TaskModel taskModel) {
-        this.taskModel = taskModel;
-        return this;
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/type/TaskInputType.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/type/TaskInputType.java
new file mode 100644
index 0000000..93d8dd6
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/type/TaskInputType.java
@@ -0,0 +1,69 @@
+package org.apache.airavata.k8s.api.server.model.task.type;
+
+import javax.persistence.*;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+@Entity
+public class TaskInputType {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    private long id;
+
+    private String name;
+    private String type;
+    private String defaultValue;
+
+    @ManyToOne
+    private TaskModelType taskModelType;
+
+    public long getId() {
+        return id;
+    }
+
+    public TaskInputType setId(long id) {
+        this.id = id;
+        return this;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public TaskInputType setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public TaskInputType setType(String type) {
+        this.type = type;
+        return this;
+    }
+
+    public String getDefaultValue() {
+        return defaultValue;
+    }
+
+    public TaskInputType setDefaultValue(String defaultValue) {
+        this.defaultValue = defaultValue;
+        return this;
+    }
+
+    public TaskModelType getTaskModelType() {
+        return taskModelType;
+    }
+
+    public TaskInputType setTaskModelType(TaskModelType taskModelType) {
+        this.taskModelType = taskModelType;
+        return this;
+    }
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/type/TaskModelType.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/type/TaskModelType.java
new file mode 100644
index 0000000..cce35c7
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/type/TaskModelType.java
@@ -0,0 +1,95 @@
+package org.apache.airavata.k8s.api.server.model.task.type;
+
+import javax.persistence.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+@Entity
+public class TaskModelType {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    private long id;
+
+    private String name;
+    private String topicName;
+    private String icon;
+
+    @OneToMany(mappedBy = "taskModelType", cascade = CascadeType.ALL)
+    private List<TaskInputType> inputTypes = new ArrayList<>();
+
+    @OneToMany(mappedBy = "taskModelType", cascade = CascadeType.ALL)
+    private List<TaskOutputType> outputTypes = new ArrayList<>();
+
+    @OneToMany(mappedBy = "taskModelType", cascade = CascadeType.ALL)
+    private List<TaskOutPortType> outPorts = new ArrayList<>();
+
+    public long getId() {
+        return id;
+    }
+
+    public TaskModelType setId(long id) {
+        this.id = id;
+        return this;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public TaskModelType setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public String getTopicName() {
+        return topicName;
+    }
+
+    public TaskModelType setTopicName(String topicName) {
+        this.topicName = topicName;
+        return this;
+    }
+
+    public List<TaskInputType> getInputTypes() {
+        return inputTypes;
+    }
+
+    public TaskModelType setInputTypes(List<TaskInputType> inputTypes) {
+        this.inputTypes = inputTypes;
+        return this;
+    }
+
+    public List<TaskOutputType> getOutputTypes() {
+        return outputTypes;
+    }
+
+    public TaskModelType setOutputTypes(List<TaskOutputType> outputTypes) {
+        this.outputTypes = outputTypes;
+        return this;
+    }
+
+    public List<TaskOutPortType> getOutPorts() {
+        return outPorts;
+    }
+
+    public TaskModelType setOutPorts(List<TaskOutPortType> outPorts) {
+        this.outPorts = outPorts;
+        return this;
+    }
+
+    public String getIcon() {
+        return icon;
+    }
+
+    public TaskModelType setIcon(String icon) {
+        this.icon = icon;
+        return this;
+    }
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/type/TaskOutPortType.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/type/TaskOutPortType.java
new file mode 100644
index 0000000..5d0a836
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/type/TaskOutPortType.java
@@ -0,0 +1,61 @@
+package org.apache.airavata.k8s.api.server.model.task.type;
+
+import javax.persistence.*;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+@Entity
+public class TaskOutPortType {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    private long id;
+
+    private String name;
+
+    @Column(name = "PORT_ORDER")
+    private int order = 0;
+
+    @ManyToOne
+    private TaskModelType taskModelType;
+
+    public long getId() {
+        return id;
+    }
+
+    public TaskOutPortType setId(long id) {
+        this.id = id;
+        return this;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public TaskOutPortType setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public int getOrder() {
+        return order;
+    }
+
+    public TaskOutPortType setOrder(int order) {
+        this.order = order;
+        return this;
+    }
+
+    public TaskModelType getTaskModelType() {
+        return taskModelType;
+    }
+
+    public TaskOutPortType setTaskModelType(TaskModelType taskModelType) {
+        this.taskModelType = taskModelType;
+        return this;
+    }
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/type/TaskOutputType.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/type/TaskOutputType.java
new file mode 100644
index 0000000..3c159a0
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/task/type/TaskOutputType.java
@@ -0,0 +1,59 @@
+package org.apache.airavata.k8s.api.server.model.task.type;
+
+import javax.persistence.*;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+@Entity
+public class TaskOutputType {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    private long id;
+
+    private String name;
+    private String type;
+
+    @ManyToOne
+    private TaskModelType taskModelType;
+
+    public long getId() {
+        return id;
+    }
+
+    public TaskOutputType setId(long id) {
+        this.id = id;
+        return this;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public TaskOutputType setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public TaskOutputType setType(String type) {
+        this.type = type;
+        return this;
+    }
+
+    public TaskModelType getTaskModelType() {
+        return taskModelType;
+    }
+
+    public TaskOutputType setTaskModelType(TaskModelType taskModelType) {
+        this.taskModelType = taskModelType;
+        return this;
+    }
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/workflow/Workflow.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/workflow/Workflow.java
new file mode 100644
index 0000000..82d6bf5
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/model/workflow/Workflow.java
@@ -0,0 +1,69 @@
+package org.apache.airavata.k8s.api.server.model.workflow;
+
+import org.apache.airavata.k8s.api.server.model.process.ProcessModel;
+
+import javax.persistence.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+@Entity
+public class Workflow {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    private long id;
+
+    private String name;
+
+    @Lob
+    @Column(length = 1000000, name = "CONTENT")
+    @Basic(fetch = FetchType.LAZY)
+    private byte[] workFlowGraph;
+
+
+    @OneToMany(mappedBy = "experiment", cascade = CascadeType.ALL)
+    private List<ProcessModel> processes = new ArrayList<>();
+
+    public long getId() {
+        return id;
+    }
+
+    public Workflow setId(long id) {
+        this.id = id;
+        return this;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Workflow setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+
+    public byte[] getWorkFlowGraph() {
+        return workFlowGraph;
+    }
+
+    public Workflow setWorkFlowGraph(byte[] workFlowGraph) {
+        this.workFlowGraph = workFlowGraph;
+        return this;
+    }
+
+    public List<ProcessModel> getProcesses() {
+        return processes;
+    }
+
+    public Workflow setProcesses(List<ProcessModel> processes) {
+        this.processes = processes;
+        return this;
+    }
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/TaskParamRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/TaskParamRepository.java
deleted file mode 100644
index 8d66c41..0000000
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/TaskParamRepository.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- *
- * 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.airavata.k8s.api.server.repository;
-
-import org.apache.airavata.k8s.api.server.model.task.TaskParam;
-import org.springframework.data.repository.CrudRepository;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-public interface TaskParamRepository extends CrudRepository<TaskParam, Long> {
-}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ApplicationDeploymentRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/application/ApplicationDeploymentRepository.java
similarity index 94%
rename from airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ApplicationDeploymentRepository.java
rename to airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/application/ApplicationDeploymentRepository.java
index 8699bea..157e3a2 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ApplicationDeploymentRepository.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/application/ApplicationDeploymentRepository.java
@@ -17,7 +17,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.airavata.k8s.api.server.repository;
+package org.apache.airavata.k8s.api.server.repository.application;
 
 import org.apache.airavata.k8s.api.server.model.application.ApplicationDeployment;
 import org.springframework.data.repository.CrudRepository;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ApplicationIfaceRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/application/ApplicationIfaceRepository.java
similarity index 94%
rename from airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ApplicationIfaceRepository.java
rename to airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/application/ApplicationIfaceRepository.java
index 57a35fb..1912528 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ApplicationIfaceRepository.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/application/ApplicationIfaceRepository.java
@@ -17,7 +17,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.airavata.k8s.api.server.repository;
+package org.apache.airavata.k8s.api.server.repository.application;
 
 import org.apache.airavata.k8s.api.server.model.application.ApplicationInterface;
 import org.springframework.data.repository.CrudRepository;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ApplicationInputRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/application/ApplicationInputRepository.java
similarity index 94%
rename from airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ApplicationInputRepository.java
rename to airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/application/ApplicationInputRepository.java
index 624fdfe..0a55f1c 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ApplicationInputRepository.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/application/ApplicationInputRepository.java
@@ -17,7 +17,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.airavata.k8s.api.server.repository;
+package org.apache.airavata.k8s.api.server.repository.application;
 
 import org.apache.airavata.k8s.api.server.model.application.ApplicationInput;
 import org.springframework.data.repository.CrudRepository;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ApplicationModuleRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/application/ApplicationModuleRepository.java
similarity index 94%
rename from airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ApplicationModuleRepository.java
rename to airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/application/ApplicationModuleRepository.java
index fd1a2ae..4fc8188 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ApplicationModuleRepository.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/application/ApplicationModuleRepository.java
@@ -17,7 +17,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.airavata.k8s.api.server.repository;
+package org.apache.airavata.k8s.api.server.repository.application;
 
 import org.apache.airavata.k8s.api.server.model.application.ApplicationModule;
 import org.springframework.data.repository.CrudRepository;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ApplicationOutputRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/application/ApplicationOutputRepository.java
similarity index 94%
rename from airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ApplicationOutputRepository.java
rename to airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/application/ApplicationOutputRepository.java
index 852179f..cea9db1 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ApplicationOutputRepository.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/application/ApplicationOutputRepository.java
@@ -17,7 +17,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.airavata.k8s.api.server.repository;
+package org.apache.airavata.k8s.api.server.repository.application;
 
 import org.apache.airavata.k8s.api.server.model.application.ApplicationOutput;
 import org.springframework.data.repository.CrudRepository;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ComputeRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/compute/ComputeRepository.java
similarity index 95%
rename from airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ComputeRepository.java
rename to airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/compute/ComputeRepository.java
index fb73dfb..c1f64c7 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ComputeRepository.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/compute/ComputeRepository.java
@@ -17,7 +17,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.airavata.k8s.api.server.repository;
+package org.apache.airavata.k8s.api.server.repository.compute;
 
 import org.apache.airavata.k8s.api.server.model.compute.ComputeResourceModel;
 import org.springframework.data.repository.CrudRepository;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/DataStoreRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/data/DataStoreRepository.java
similarity index 95%
rename from airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/DataStoreRepository.java
rename to airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/data/DataStoreRepository.java
index ec584c3..85e8596 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/DataStoreRepository.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/data/DataStoreRepository.java
@@ -17,7 +17,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.airavata.k8s.api.server.repository;
+package org.apache.airavata.k8s.api.server.repository.data;
 
 import org.apache.airavata.k8s.api.server.model.data.DataStoreModel;
 import org.springframework.data.repository.CrudRepository;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ExperimentInputDataRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/experiment/ExperimentInputDataRepository.java
similarity index 94%
rename from airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ExperimentInputDataRepository.java
rename to airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/experiment/ExperimentInputDataRepository.java
index 9087da7..1d84e75 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ExperimentInputDataRepository.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/experiment/ExperimentInputDataRepository.java
@@ -17,7 +17,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.airavata.k8s.api.server.repository;
+package org.apache.airavata.k8s.api.server.repository.experiment;
 
 import org.apache.airavata.k8s.api.server.model.experiment.ExperimentInputData;
 import org.springframework.data.repository.CrudRepository;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ExperimentOutputDataRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/experiment/ExperimentOutputDataRepository.java
similarity index 94%
rename from airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ExperimentOutputDataRepository.java
rename to airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/experiment/ExperimentOutputDataRepository.java
index b3b9392..8564cdb 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ExperimentOutputDataRepository.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/experiment/ExperimentOutputDataRepository.java
@@ -17,7 +17,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.airavata.k8s.api.server.repository;
+package org.apache.airavata.k8s.api.server.repository.experiment;
 
 import org.apache.airavata.k8s.api.server.model.experiment.ExperimentOutputData;
 import org.springframework.data.repository.CrudRepository;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ExperimentRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/experiment/ExperimentRepository.java
similarity index 94%
rename from airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ExperimentRepository.java
rename to airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/experiment/ExperimentRepository.java
index ca98567..26078e5 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ExperimentRepository.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/experiment/ExperimentRepository.java
@@ -17,7 +17,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.airavata.k8s.api.server.repository;
+package org.apache.airavata.k8s.api.server.repository.experiment;
 
 import org.apache.airavata.k8s.api.server.model.experiment.Experiment;
 import org.springframework.data.repository.CrudRepository;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ExperimentStatusRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/experiment/ExperimentStatusRepository.java
similarity index 94%
rename from airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ExperimentStatusRepository.java
rename to airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/experiment/ExperimentStatusRepository.java
index 0b795c1..63cd3f0 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ExperimentStatusRepository.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/experiment/ExperimentStatusRepository.java
@@ -17,7 +17,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.airavata.k8s.api.server.repository;
+package org.apache.airavata.k8s.api.server.repository.experiment;
 
 import org.apache.airavata.k8s.api.server.model.experiment.ExperimentStatus;
 import org.springframework.data.repository.CrudRepository;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ProcessRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/process/ProcessRepository.java
similarity index 94%
rename from airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ProcessRepository.java
rename to airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/process/ProcessRepository.java
index d0dc311..62c0aaa 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ProcessRepository.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/process/ProcessRepository.java
@@ -17,7 +17,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.airavata.k8s.api.server.repository;
+package org.apache.airavata.k8s.api.server.repository.process;
 
 import org.apache.airavata.k8s.api.server.model.process.ProcessModel;
 import org.springframework.data.repository.CrudRepository;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ProcessStatusRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/process/ProcessStatusRepository.java
similarity index 94%
rename from airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ProcessStatusRepository.java
rename to airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/process/ProcessStatusRepository.java
index bb11a9a..0c01b8a 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/ProcessStatusRepository.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/process/ProcessStatusRepository.java
@@ -17,7 +17,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.airavata.k8s.api.server.repository;
+package org.apache.airavata.k8s.api.server.repository.process;
 
 import org.apache.airavata.k8s.api.server.model.process.ProcessStatus;
 import org.springframework.data.repository.CrudRepository;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/TaskDAGRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/TaskDAGRepository.java
new file mode 100644
index 0000000..b2cb7fa
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/TaskDAGRepository.java
@@ -0,0 +1,13 @@
+package org.apache.airavata.k8s.api.server.repository.task;
+
+import org.apache.airavata.k8s.api.server.model.task.TaskDAG;
+import org.springframework.data.repository.CrudRepository;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public interface TaskDAGRepository extends CrudRepository<TaskDAG, Long> {
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/TaskInputRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/TaskInputRepository.java
new file mode 100644
index 0000000..0576245
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/TaskInputRepository.java
@@ -0,0 +1,13 @@
+package org.apache.airavata.k8s.api.server.repository.task;
+
+import org.apache.airavata.k8s.api.server.model.task.TaskInput;
+import org.springframework.data.repository.CrudRepository;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public interface TaskInputRepository extends CrudRepository<TaskInput, Long> {
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/TaskOutPortRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/TaskOutPortRepository.java
new file mode 100644
index 0000000..63ffd89
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/TaskOutPortRepository.java
@@ -0,0 +1,17 @@
+package org.apache.airavata.k8s.api.server.repository.task;
+
+import org.apache.airavata.k8s.api.server.model.task.TaskOutPort;
+import org.springframework.data.repository.CrudRepository;
+
+import java.util.Optional;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public interface TaskOutPortRepository extends CrudRepository<TaskOutPort, Long> {
+    Optional<TaskOutPort> findByName(String name);
+    Optional<TaskOutPort> findByReferenceIdAndTaskModel_Id(int referenceId, long taskId);
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/TaskOutputRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/TaskOutputRepository.java
new file mode 100644
index 0000000..62a87af
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/TaskOutputRepository.java
@@ -0,0 +1,13 @@
+package org.apache.airavata.k8s.api.server.repository.task;
+
+import org.apache.airavata.k8s.api.server.model.task.TaskOutput;
+import org.springframework.data.repository.CrudRepository;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public interface TaskOutputRepository extends CrudRepository<TaskOutput, Long> {
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/TaskRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/TaskRepository.java
similarity index 95%
rename from airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/TaskRepository.java
rename to airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/TaskRepository.java
index 2701c33..f20e936 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/TaskRepository.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/TaskRepository.java
@@ -17,7 +17,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.airavata.k8s.api.server.repository;
+package org.apache.airavata.k8s.api.server.repository.task;
 
 import org.apache.airavata.k8s.api.server.model.task.TaskModel;
 import org.springframework.data.repository.CrudRepository;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/TaskStatusRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/TaskStatusRepository.java
similarity index 95%
rename from airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/TaskStatusRepository.java
rename to airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/TaskStatusRepository.java
index b79ad2b..05096a9 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/TaskStatusRepository.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/TaskStatusRepository.java
@@ -17,7 +17,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.airavata.k8s.api.server.repository;
+package org.apache.airavata.k8s.api.server.repository.task;
 
 import org.apache.airavata.k8s.api.server.model.task.TaskStatus;
 import org.springframework.data.repository.CrudRepository;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/type/TaskInputTypeRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/type/TaskInputTypeRepository.java
new file mode 100644
index 0000000..fe66f66
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/type/TaskInputTypeRepository.java
@@ -0,0 +1,13 @@
+package org.apache.airavata.k8s.api.server.repository.task.type;
+
+import org.apache.airavata.k8s.api.server.model.task.type.TaskInputType;
+import org.springframework.data.repository.CrudRepository;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public interface TaskInputTypeRepository extends CrudRepository<TaskInputType, Long> {
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/type/TaskOutPortTypeRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/type/TaskOutPortTypeRepository.java
new file mode 100644
index 0000000..98d1715
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/type/TaskOutPortTypeRepository.java
@@ -0,0 +1,13 @@
+package org.apache.airavata.k8s.api.server.repository.task.type;
+
+import org.apache.airavata.k8s.api.server.model.task.type.TaskOutPortType;
+import org.springframework.data.repository.CrudRepository;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public interface TaskOutPortTypeRepository extends CrudRepository<TaskOutPortType, Long> {
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/type/TaskOutputTypeRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/type/TaskOutputTypeRepository.java
new file mode 100644
index 0000000..c28d6da
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/type/TaskOutputTypeRepository.java
@@ -0,0 +1,13 @@
+package org.apache.airavata.k8s.api.server.repository.task.type;
+
+import org.apache.airavata.k8s.api.server.model.task.type.TaskOutputType;
+import org.springframework.data.repository.CrudRepository;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public interface TaskOutputTypeRepository extends CrudRepository<TaskOutputType, Long> {
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/type/TaskTypeRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/type/TaskTypeRepository.java
new file mode 100644
index 0000000..a28e374
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/task/type/TaskTypeRepository.java
@@ -0,0 +1,17 @@
+package org.apache.airavata.k8s.api.server.repository.task.type;
+
+import org.apache.airavata.k8s.api.server.model.task.type.TaskModelType;
+import org.springframework.data.repository.CrudRepository;
+
+import java.util.Optional;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public interface TaskTypeRepository extends CrudRepository<TaskModelType, Long> {
+    Optional<TaskModelType> findById(long id);
+    Optional<TaskModelType> findByName(String name);
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/workflow/WorkflowRepository.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/workflow/WorkflowRepository.java
new file mode 100644
index 0000000..259f41e
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/repository/workflow/WorkflowRepository.java
@@ -0,0 +1,16 @@
+package org.apache.airavata.k8s.api.server.repository.workflow;
+
+import org.apache.airavata.k8s.api.server.model.workflow.Workflow;
+import org.springframework.data.repository.CrudRepository;
+
+import java.util.Optional;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public interface WorkflowRepository extends CrudRepository<Workflow, Long> {
+    public Optional<Workflow> findById(long id);
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ApplicationDeploymentService.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ApplicationDeploymentService.java
index 941ee6e..93216bc 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ApplicationDeploymentService.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ApplicationDeploymentService.java
@@ -21,9 +21,9 @@ package org.apache.airavata.k8s.api.server.service;
 
 import org.apache.airavata.k8s.api.server.ServerRuntimeException;
 import org.apache.airavata.k8s.api.server.model.application.ApplicationDeployment;
-import org.apache.airavata.k8s.api.server.repository.ApplicationDeploymentRepository;
-import org.apache.airavata.k8s.api.server.repository.ApplicationModuleRepository;
-import org.apache.airavata.k8s.api.server.repository.ComputeRepository;
+import org.apache.airavata.k8s.api.server.repository.application.ApplicationDeploymentRepository;
+import org.apache.airavata.k8s.api.server.repository.application.ApplicationModuleRepository;
+import org.apache.airavata.k8s.api.server.repository.compute.ComputeRepository;
 import org.apache.airavata.k8s.api.resources.application.ApplicationDeploymentResource;
 import org.apache.airavata.k8s.api.server.service.util.ToResourceUtil;
 import org.springframework.beans.factory.annotation.Autowired;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ApplicationIfaceService.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ApplicationIfaceService.java
index e95b651..7472c36 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ApplicationIfaceService.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ApplicationIfaceService.java
@@ -23,10 +23,10 @@ import org.apache.airavata.k8s.api.server.ServerRuntimeException;
 import org.apache.airavata.k8s.api.server.model.application.ApplicationInput;
 import org.apache.airavata.k8s.api.server.model.application.ApplicationInterface;
 import org.apache.airavata.k8s.api.server.model.application.ApplicationOutput;
-import org.apache.airavata.k8s.api.server.repository.ApplicationIfaceRepository;
-import org.apache.airavata.k8s.api.server.repository.ApplicationInputRepository;
-import org.apache.airavata.k8s.api.server.repository.ApplicationModuleRepository;
-import org.apache.airavata.k8s.api.server.repository.ApplicationOutputRepository;
+import org.apache.airavata.k8s.api.server.repository.application.ApplicationIfaceRepository;
+import org.apache.airavata.k8s.api.server.repository.application.ApplicationInputRepository;
+import org.apache.airavata.k8s.api.server.repository.application.ApplicationModuleRepository;
+import org.apache.airavata.k8s.api.server.repository.application.ApplicationOutputRepository;
 import org.apache.airavata.k8s.api.resources.application.ApplicationIfaceResource;
 import org.apache.airavata.k8s.api.server.service.util.ToResourceUtil;
 import org.springframework.beans.factory.annotation.Autowired;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ApplicationModuleService.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ApplicationModuleService.java
index e658c42..4fd6543 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ApplicationModuleService.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ApplicationModuleService.java
@@ -20,7 +20,7 @@
 package org.apache.airavata.k8s.api.server.service;
 
 import org.apache.airavata.k8s.api.server.model.application.ApplicationModule;
-import org.apache.airavata.k8s.api.server.repository.ApplicationModuleRepository;
+import org.apache.airavata.k8s.api.server.repository.application.ApplicationModuleRepository;
 import org.apache.airavata.k8s.api.resources.application.ApplicationModuleResource;
 import org.apache.airavata.k8s.api.server.service.util.ToResourceUtil;
 import org.springframework.beans.factory.annotation.Autowired;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ComputeResourceService.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ComputeResourceService.java
index bceda0b..a3d7525 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ComputeResourceService.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ComputeResourceService.java
@@ -20,7 +20,7 @@
 package org.apache.airavata.k8s.api.server.service;
 
 import org.apache.airavata.k8s.api.server.model.compute.ComputeResourceModel;
-import org.apache.airavata.k8s.api.server.repository.ComputeRepository;
+import org.apache.airavata.k8s.api.server.repository.compute.ComputeRepository;
 import org.apache.airavata.k8s.api.resources.compute.ComputeResource;
 import org.apache.airavata.k8s.api.server.service.util.ToResourceUtil;
 import org.springframework.beans.factory.annotation.Autowired;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ExperimentService.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ExperimentService.java
index 1ded541..5f67efd 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ExperimentService.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ExperimentService.java
@@ -24,8 +24,13 @@ import org.apache.airavata.k8s.api.server.model.experiment.Experiment;
 import org.apache.airavata.k8s.api.server.model.experiment.ExperimentInputData;
 import org.apache.airavata.k8s.api.server.model.experiment.ExperimentOutputData;
 import org.apache.airavata.k8s.api.server.model.experiment.ExperimentStatus;
-import org.apache.airavata.k8s.api.server.repository.*;
 import org.apache.airavata.k8s.api.resources.experiment.ExperimentResource;
+import org.apache.airavata.k8s.api.server.repository.application.ApplicationDeploymentRepository;
+import org.apache.airavata.k8s.api.server.repository.application.ApplicationIfaceRepository;
+import org.apache.airavata.k8s.api.server.repository.experiment.ExperimentInputDataRepository;
+import org.apache.airavata.k8s.api.server.repository.experiment.ExperimentOutputDataRepository;
+import org.apache.airavata.k8s.api.server.repository.experiment.ExperimentRepository;
+import org.apache.airavata.k8s.api.server.repository.experiment.ExperimentStatusRepository;
 import org.apache.airavata.k8s.api.server.service.messaging.MessagingService;
 import org.apache.airavata.k8s.api.server.service.util.ToResourceUtil;
 import org.springframework.beans.factory.annotation.Autowired;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ProcessService.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ProcessService.java
index 532a1d5..d2b93aa 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ProcessService.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/ProcessService.java
@@ -24,9 +24,10 @@ import org.apache.airavata.k8s.api.server.ServerRuntimeException;
 import org.apache.airavata.k8s.api.server.model.process.ProcessModel;
 import org.apache.airavata.k8s.api.server.model.process.ProcessStatus;
 import org.apache.airavata.k8s.api.server.model.task.TaskModel;
-import org.apache.airavata.k8s.api.server.repository.ProcessRepository;
+import org.apache.airavata.k8s.api.server.repository.process.ProcessRepository;
 import org.apache.airavata.k8s.api.resources.process.ProcessResource;
-import org.apache.airavata.k8s.api.server.repository.ProcessStatusRepository;
+import org.apache.airavata.k8s.api.server.repository.process.ProcessStatusRepository;
+import org.apache.airavata.k8s.api.server.service.task.TaskService;
 import org.apache.airavata.k8s.api.server.service.util.ToResourceUtil;
 import org.springframework.stereotype.Service;
 
@@ -47,6 +48,8 @@ public class ProcessService {
     private ExperimentService experimentService;
     private TaskService taskService;
 
+    private WorkflowService workflowService;
+
     public ProcessService(ProcessRepository processRepository,
                           ProcessStatusRepository processStatusRepository,
                           ExperimentService experimentService,
@@ -64,9 +67,21 @@ public class ProcessService {
         processModel.setId(resource.getId());
         processModel.setCreationTime(resource.getCreationTime());
         processModel.setLastUpdateTime(resource.getLastUpdateTime());
-        processModel.setExperiment(experimentService.findEntityById(resource.getExperimentId())
-                .orElseThrow(() -> new ServerRuntimeException("Can not find experiment with id " +
-                        resource.getExperimentId())));
+        processModel.setName(resource.getName());
+        processModel.setProcessType(ProcessModel.ProcessType.valueOf(resource.getProcessType()));
+
+        if (resource.getExperimentId() != 0) {
+            processModel.setExperiment(experimentService.findEntityById(resource.getExperimentId())
+                    .orElseThrow(() -> new ServerRuntimeException("Can not find experiment with id " +
+                            resource.getExperimentId())));
+        }
+
+        if (resource.getWorkflowId() != 0) {
+            processModel.setWorkflow(workflowService.findEntityById(resource.getWorkflowId())
+                    .orElseThrow(() -> new ServerRuntimeException("Can not find workflow with id " +
+                            resource.getWorkflowId())));
+        }
+
         processModel.setExperimentDataDir(resource.getExperimentDataDir());
 
         ProcessModel saved = processRepository.save(processModel);
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/WorkflowService.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/WorkflowService.java
new file mode 100644
index 0000000..6090c90
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/WorkflowService.java
@@ -0,0 +1,129 @@
+package org.apache.airavata.k8s.api.server.service;
+
+import org.apache.airavata.k8s.api.resources.process.ProcessResource;
+import org.apache.airavata.k8s.api.resources.workflow.WorkflowResource;
+import org.apache.airavata.k8s.api.server.ServerRuntimeException;
+import org.apache.airavata.k8s.api.server.model.process.ProcessModel;
+import org.apache.airavata.k8s.api.server.model.task.TaskDAG;
+import org.apache.airavata.k8s.api.server.model.task.TaskModel;
+import org.apache.airavata.k8s.api.server.model.task.TaskOutPort;
+import org.apache.airavata.k8s.api.server.model.workflow.Workflow;
+import org.apache.airavata.k8s.api.server.repository.task.TaskDAGRepository;
+import org.apache.airavata.k8s.api.server.repository.task.TaskOutPortRepository;
+import org.apache.airavata.k8s.api.server.repository.task.TaskRepository;
+import org.apache.airavata.k8s.api.server.repository.workflow.WorkflowRepository;
+import org.apache.airavata.k8s.api.server.service.task.TaskService;
+import org.apache.airavata.k8s.api.server.service.util.GraphParser;
+import org.apache.airavata.k8s.api.server.service.util.ToResourceUtil;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+@Service
+public class WorkflowService {
+
+    private ProcessService processService;
+    private TaskService taskService;
+
+    private WorkflowRepository workflowRepository;
+    private TaskOutPortRepository taskOutPortRepository;
+    private TaskRepository taskRepository;
+    private TaskDAGRepository taskDAGRepository;
+
+    public WorkflowService(ProcessService processService,
+                           TaskService taskService,
+                           WorkflowRepository workflowRepository,
+                           TaskOutPortRepository taskOutPortRepository,
+                           TaskRepository taskRepository,
+                           TaskDAGRepository taskDAGRepository) {
+
+        this.processService = processService;
+        this.taskService = taskService;
+        this.workflowRepository = workflowRepository;
+        this.taskOutPortRepository = taskOutPortRepository;
+        this.taskRepository = taskRepository;
+        this.taskDAGRepository = taskDAGRepository;
+    }
+
+    public long createWorkflow(WorkflowResource resource) {
+        Workflow workflow = new Workflow();
+        workflow.setName(resource.getName());
+        workflow.setWorkFlowGraph(resource.getWorkflowGraphXML().getBytes());
+
+        Workflow saved = workflowRepository.save(workflow);
+        return saved.getId();
+    }
+
+    public long launchWorkflow(long id) {
+        Workflow workflow = this.workflowRepository.findById(id)
+                .orElseThrow(() -> new ServerRuntimeException("Workflow with id " + id + "can not be found"));
+
+
+        long processId = processService.create(new ProcessResource()
+                .setName("Workflow Process : " + workflow.getName() + "-" + UUID.randomUUID().toString())
+                .setCreationTime(System.currentTimeMillis()).setProcessType("WORKFLOW"));
+
+        try {
+            GraphParser.ParseResult parseResult = GraphParser.parse(new String(workflow.getWorkFlowGraph()));
+            parseResult.getTasks().forEach((mockId, task) -> {
+                task.getTaskResource().setParentProcessId(processId);
+
+                for (GraphParser.OutPort outPort : task.getOutPorts().values()) {
+                    if (outPort.getNextPort() != null && outPort.getNextPort().getParentOperation() != null) {
+                        if ("Stop".equals(outPort.getNextPort().getParentOperation().getName())) {
+                            task.getTaskResource().setStoppingTask(true);
+                        }
+                    }
+                }
+
+                for (GraphParser.InPort inPort : task.getInPorts().values()) {
+                    for (GraphParser.OutPort outPort : inPort.getPreviousPorts().values()) {
+                        if (outPort.getParentOperation() != null && "Start".equals(outPort.getParentOperation().getName())) {
+                            task.getTaskResource().setStartingTask(true);
+                        }
+                    }
+                }
+
+                long taskId = taskService.create(task.getTaskResource());
+                task.getTaskResource().setId(taskId);
+            });
+
+            parseResult.getEdgeCache().forEach(((outPort, inPort) -> {
+                if (outPort.getParentOperation() != null && outPort.getNextPort().getParentOperation() != null) {
+                    Optional<TaskOutPort> sourceOutPort = taskOutPortRepository
+                            .findByReferenceIdAndTaskModel_Id(outPort.getId(), outPort.getParentTask().getId());
+                    Optional<TaskModel> targetTask = taskRepository.findById(inPort.getParentTask().getId());
+
+                    taskDAGRepository.save(new TaskDAG()
+                            .setSourceOutPort(sourceOutPort.get())
+                            .setTargetTask(targetTask.get()));
+                }
+            }));
+
+        } catch (Exception e) {
+            throw new ServerRuntimeException("Failed to create workflow", e);
+        }
+        return 0;
+    }
+
+    public List<WorkflowResource> getAll() {
+        List<WorkflowResource> workflowResources = new ArrayList<>();
+        Iterable<Workflow> workFlows = this.workflowRepository.findAll();
+        Optional.ofNullable(workFlows)
+                .ifPresent(wfs -> wfs.forEach(wf -> workflowResources.add(ToResourceUtil.toResource(wf).get())));
+        return workflowResources;
+    }
+
+    public Optional<Workflow> findEntityById(long id) {
+        return this.workflowRepository.findById(id);
+    }
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/data/DataStoreService.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/data/DataStoreService.java
index 27fceb1..272ab33 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/data/DataStoreService.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/data/DataStoreService.java
@@ -21,9 +21,9 @@ package org.apache.airavata.k8s.api.server.service.data;
 
 import org.apache.airavata.k8s.api.resources.data.DataEntryResource;
 import org.apache.airavata.k8s.api.server.model.data.DataStoreModel;
-import org.apache.airavata.k8s.api.server.repository.DataStoreRepository;
-import org.apache.airavata.k8s.api.server.repository.ExperimentOutputDataRepository;
-import org.apache.airavata.k8s.api.server.repository.TaskRepository;
+import org.apache.airavata.k8s.api.server.repository.data.DataStoreRepository;
+import org.apache.airavata.k8s.api.server.repository.experiment.ExperimentOutputDataRepository;
+import org.apache.airavata.k8s.api.server.repository.task.TaskRepository;
 import org.springframework.stereotype.Service;
 
 import java.util.ArrayList;
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/TaskService.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/task/TaskService.java
similarity index 53%
rename from airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/TaskService.java
rename to airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/task/TaskService.java
index 11a3b91..d1b3828 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/TaskService.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/task/TaskService.java
@@ -17,22 +17,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.airavata.k8s.api.server.service;
+package org.apache.airavata.k8s.api.server.service.task;
 
 import org.apache.airavata.k8s.api.resources.task.TaskResource;
 import org.apache.airavata.k8s.api.resources.task.TaskStatusResource;
 import org.apache.airavata.k8s.api.server.ServerRuntimeException;
-import org.apache.airavata.k8s.api.server.model.task.TaskModel;
-import org.apache.airavata.k8s.api.server.model.task.TaskParam;
-import org.apache.airavata.k8s.api.server.model.task.TaskStatus;
-import org.apache.airavata.k8s.api.server.repository.ProcessRepository;
-import org.apache.airavata.k8s.api.server.repository.TaskParamRepository;
-import org.apache.airavata.k8s.api.server.repository.TaskRepository;
-import org.apache.airavata.k8s.api.server.repository.TaskStatusRepository;
+import org.apache.airavata.k8s.api.server.model.task.*;
+import org.apache.airavata.k8s.api.server.repository.process.ProcessRepository;
+import org.apache.airavata.k8s.api.server.repository.task.*;
+import org.apache.airavata.k8s.api.server.repository.task.type.TaskTypeRepository;
 import org.apache.airavata.k8s.api.server.service.util.ToResourceUtil;
 import org.springframework.stereotype.Service;
 
 import java.util.Optional;
+import java.util.UUID;
 
 /**
  * TODO: Class level comments please
@@ -45,40 +43,77 @@ public class TaskService {
 
     private ProcessRepository processRepository;
     private TaskRepository taskRepository;
-    private TaskParamRepository taskParamRepository;
+    private TaskInputRepository taskInputRepository;
+    private TaskOutputRepository taskOutputRepository;
     private TaskStatusRepository taskStatusRepository;
+    private TaskTypeRepository taskTypeRepository;
+    private TaskOutPortRepository taskOutPortRepository;
+    private TaskDAGRepository taskDAGRepository;
+
 
     public TaskService(ProcessRepository processRepository,
                        TaskRepository taskRepository,
-                       TaskParamRepository taskParamRepository,
-                       TaskStatusRepository taskStatusRepository) {
+                       TaskInputRepository taskInputRepository,
+                       TaskOutputRepository taskOutputRepository,
+                       TaskStatusRepository taskStatusRepository,
+                       TaskTypeRepository taskTypeRepository,
+                       TaskOutPortRepository taskOutPortRepository,
+                       TaskDAGRepository taskDAGRepository) {
 
         this.processRepository = processRepository;
         this.taskRepository = taskRepository;
-        this.taskParamRepository = taskParamRepository;
+        this.taskInputRepository = taskInputRepository;
+        this.taskOutputRepository = taskOutputRepository;
         this.taskStatusRepository = taskStatusRepository;
+        this.taskTypeRepository = taskTypeRepository;
+        this.taskOutPortRepository = taskOutPortRepository;
+        this.taskDAGRepository = taskDAGRepository;
     }
 
     public long create(TaskResource resource) {
         TaskModel taskModel = new TaskModel();
+        taskModel.setName(resource.getName());
         taskModel.setCreationTime(resource.getCreationTime());
         taskModel.setLastUpdateTime(resource.getLastUpdateTime());
         taskModel.setOrderIndex(resource.getOrder());
+        taskModel.setStartingTask(resource.isStartingTask());
+        taskModel.setStoppingTask(resource.isStoppingTask());
         taskModel.setTaskDetail(resource.getTaskDetail());
         taskModel.setParentProcess(processRepository.findById(resource.getParentProcessId())
                 .orElseThrow(() -> new ServerRuntimeException("Can not find process with id " +
                         resource.getParentProcessId())));
-        taskModel.setTaskType(TaskModel.TaskTypes.valueOf(resource.getTaskType()));
+        taskModel.setTaskType(taskTypeRepository.findById(resource.getTaskTypeId())
+                .orElseThrow(() -> new ServerRuntimeException("Can not find task type with id " +
+                resource.getTaskTypeId())));
 
         TaskModel savedTask = taskRepository.save(taskModel);
 
-        Optional.ofNullable(resource.getTaskParams()).ifPresent(params -> params.forEach(param -> {
-            TaskParam taskParam = new TaskParam();
-            taskParam.setKey(param.getKey());
-            taskParam.setValue(param.getValue());
-            taskParam.setTaskModel(savedTask);
-            taskParamRepository.save(taskParam);
+        Optional.ofNullable(resource.getInputs()).ifPresent(inputs -> inputs.forEach(input -> {
+            TaskInput taskInput = new TaskInput();
+            taskInput.setName(input.getName());
+            taskInput.setValue(input.getValue());
+            taskInput.setType(input.getType());
+            taskInput.setImportFrom(input.getImportFrom());
+            taskInput.setTaskModel(savedTask);
+            taskInputRepository.save(taskInput);
+        }));
+
+        Optional.ofNullable(resource.getOutputs()).ifPresent(outputs -> outputs.forEach(output -> {
+            TaskOutput taskOutput = new TaskOutput();
+            taskOutput.setName(output.getName());
+            taskOutput.setValue(output.getValue());
+            taskOutput.setType(output.getType());
+            taskOutput.setExportTo(output.getExportTo());
+            taskOutput.setTaskModel(savedTask);
+            taskOutputRepository.save(taskOutput);
+        }));
 
+        Optional.ofNullable(resource.getOutPorts()).ifPresent(outPorts -> outPorts.forEach(outPort -> {
+            TaskOutPort taskOutPort = new TaskOutPort();
+            taskOutPort.setName(outPort.getName());
+            taskOutPort.setReferenceId(outPort.getReferenceId());
+            taskOutPort.setTaskModel(taskModel);
+            taskOutPortRepository.save(taskOutPort);
         }));
         return savedTask.getId();
     }
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/task/type/TaskTypeService.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/task/type/TaskTypeService.java
new file mode 100644
index 0000000..d2ae0f1
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/task/type/TaskTypeService.java
@@ -0,0 +1,94 @@
+package org.apache.airavata.k8s.api.server.service.task.type;
+
+import org.apache.airavata.k8s.api.resources.task.type.TaskTypeResource;
+import org.apache.airavata.k8s.api.server.model.task.type.TaskInputType;
+import org.apache.airavata.k8s.api.server.model.task.type.TaskModelType;
+import org.apache.airavata.k8s.api.server.model.task.type.TaskOutPortType;
+import org.apache.airavata.k8s.api.server.model.task.type.TaskOutputType;
+import org.apache.airavata.k8s.api.server.repository.task.type.TaskInputTypeRepository;
+import org.apache.airavata.k8s.api.server.repository.task.type.TaskOutPortTypeRepository;
+import org.apache.airavata.k8s.api.server.repository.task.type.TaskOutputTypeRepository;
+import org.apache.airavata.k8s.api.server.repository.task.type.TaskTypeRepository;
+import org.apache.airavata.k8s.api.server.service.util.ToResourceUtil;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+@Service
+public class TaskTypeService {
+
+    private TaskTypeRepository taskTypeRepository;
+    private TaskInputTypeRepository taskInputTypeRepository;
+    private TaskOutputTypeRepository taskOutputTypeRepository;
+    private TaskOutPortTypeRepository taskOutPortTypeRepository;
+
+    public TaskTypeService(TaskTypeRepository taskTypeRepository,
+                           TaskInputTypeRepository taskInputTypeRepository,
+                           TaskOutputTypeRepository taskOutputTypeRepository,
+                           TaskOutPortTypeRepository taskOutPortTypeRepository) {
+
+        this.taskTypeRepository = taskTypeRepository;
+        this.taskInputTypeRepository = taskInputTypeRepository;
+        this.taskOutputTypeRepository = taskOutputTypeRepository;
+        this.taskOutPortTypeRepository = taskOutPortTypeRepository;
+    }
+
+    public long create(TaskTypeResource resource) {
+
+        Optional<TaskModelType> taskType = taskTypeRepository.findByName(resource.getName());
+
+        if (taskType.isPresent()) {
+            return taskType.get().getId();
+        }
+
+        TaskModelType taskModelType = new TaskModelType();
+        taskModelType.setName(resource.getName());
+        taskModelType.setTopicName(resource.getTopicName());
+        taskModelType.setIcon(resource.getIcon());
+        TaskModelType savedTaskType = taskTypeRepository.save(taskModelType);
+
+        Optional.ofNullable(resource.getInputTypes()).ifPresent(inputs -> inputs.forEach(input -> {
+            TaskInputType inputType = new TaskInputType();
+            inputType.setName(input.getName());
+            inputType.setType(input.getType());
+            inputType.setDefaultValue(input.getDefaultValue());
+            inputType.setTaskModelType(savedTaskType);
+            taskInputTypeRepository.save(inputType);
+        }));
+
+        Optional.ofNullable(resource.getOutputTypes()).ifPresent(outputs -> outputs.forEach(output -> {
+            TaskOutputType outputType = new TaskOutputType();
+            outputType.setName(output.getName());
+            outputType.setType(output.getType());
+            outputType.setTaskModelType(savedTaskType);
+            taskOutputTypeRepository.save(outputType);
+        }));
+
+        Optional.ofNullable(resource.getOutPorts()).ifPresent(outPorts -> outPorts.forEach(outPort -> {
+            TaskOutPortType outPortType = new TaskOutPortType();
+            outPortType.setName(outPort.getName());
+            outPortType.setOrder(outPort.getOrder());
+            outPortType.setTaskModelType(savedTaskType);
+            taskOutPortTypeRepository.save(outPortType);
+        }));
+
+        return savedTaskType.getId();
+    }
+
+    public List<TaskTypeResource> getAll() {
+        List<TaskTypeResource> types = new ArrayList<>();
+        Optional.ofNullable(taskTypeRepository.findAll())
+                .ifPresent(taskModelTypes -> taskModelTypes.forEach(taskModelType -> {
+                    types.add(ToResourceUtil.toResource(taskModelType).get());
+                }));
+        return types;
+    }
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/util/GraphParser.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/util/GraphParser.java
new file mode 100644
index 0000000..9cf1630
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/util/GraphParser.java
@@ -0,0 +1,440 @@
+package org.apache.airavata.k8s.api.server.service.util;
+
+import org.apache.airavata.k8s.api.resources.task.TaskInputResource;
+import org.apache.airavata.k8s.api.resources.task.TaskOutPortResource;
+import org.apache.airavata.k8s.api.resources.task.TaskOutputResource;
+import org.apache.airavata.k8s.api.resources.task.TaskResource;
+import org.w3c.dom.*;
+import org.xml.sax.InputSource;
+
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.File;
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public class GraphParser {
+
+    public static ParseResult parse(String content) throws Exception {
+
+        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
+        Document doc = dBuilder.parse(new InputSource(new StringReader(content)));
+        doc.getDocumentElement().normalize();
+
+        NodeList nList = doc.getElementsByTagName("root");
+        Node rootNode = nList.item(0);
+
+        NodeList elementNodeList = rootNode.getChildNodes();
+
+        ParseResult result = new ParseResult();
+
+        for (int temp = 0; temp < elementNodeList.getLength(); temp++) {
+
+            Node nNode = elementNodeList.item(temp);
+
+            if (nNode.getNodeType() == Node.ELEMENT_NODE) {
+                Element eElement = (Element) nNode;
+
+                if ("ProcessingElement".equals(eElement.getTagName())) {
+                    TaskResource taskResource = new TaskResource();
+                    NamedNodeMap attributes = eElement.getAttributes();
+                    int id = -1;
+                    for (int i = 0; i < attributes.getLength(); i++) {
+                        Node attr = attributes.item(i);
+
+                        if ("name".equals(attr.getNodeName())) {
+                            taskResource.setName(attr.getNodeValue());
+
+                        } else if ("Type".equals(attr.getNodeName())) {
+                            taskResource.setTaskTypeId(Long.parseLong(attr.getNodeValue()));
+
+                        } else if (attr.getNodeName().startsWith("in-") || attr.getNodeName().startsWith("out-")) {
+
+                        } else if ("id".equals(attr.getNodeName())) {
+                            id = Integer.parseInt(attr.getNodeValue());
+
+                        } else if (attr.getNodeName().startsWith("output-")) {
+                            TaskOutputResource outputResource = new TaskOutputResource();
+                            outputResource.setName(attr.getNodeName().substring(7));
+                            outputResource.setValue(attr.getNodeValue());
+
+                        } else {
+                            TaskInputResource inputResource = new TaskInputResource();
+                            inputResource.setName(attr.getNodeName());
+                            inputResource.setValue(attr.getNodeValue());
+                            taskResource.getInputs().add(inputResource);
+                        }
+
+                    }
+                    Task task = new Task();
+                    task.id = id;
+                    task.taskResource = taskResource;
+                    result.tasks.put(id, task);
+
+                } else if ("Operation".equals(eElement.getTagName())) {
+                    NamedNodeMap attributes = eElement.getAttributes();
+                    int id = -1;
+                    String name = null;
+                    for (int i = 0; i < attributes.getLength(); i++) {
+                        Node attr = attributes.item(i);
+
+                        if ("name".equals(attr.getNodeName())) {
+                            name = attr.getNodeValue();
+                        } else if ("id".equals(attr.getNodeName())) {
+                            id = Integer.parseInt(attr.getNodeValue());
+                        }
+
+                    }
+
+                    Operation operation = new Operation();
+                    operation.id = id;
+                    operation.name = name;
+                    result.operations.put(id, operation);
+                } else if ("InPort".equals(eElement.getTagName())) {
+
+                    int id = Integer.parseInt(eElement.getAttribute("id"));
+                    String name = eElement.getAttribute("name");
+
+                    Element mxCell = null;
+
+                    NodeList childNodes = eElement.getChildNodes();
+                    for (int i = 0 ; i < childNodes.getLength(); i++) {
+                        Node tempInPort = childNodes.item(i);
+                        if (tempInPort.getNodeType() == Node.ELEMENT_NODE) {
+                            Element tmpInElement = (Element) tempInPort;
+                            if ("mxCell".equals(tmpInElement.getTagName())) {
+                                mxCell = tmpInElement;
+                                break;
+                            }
+                        }
+                    }
+
+                    int parentId = Integer.parseInt(mxCell.getAttribute("parent"));
+
+                    InPort inPort = new InPort();
+                    inPort.id = id;
+                    inPort.name = name;
+                    result.portCache.put(id, inPort);
+
+                    if (result.tasks.containsKey(parentId)) {
+                        inPort.parentTask = result.tasks.get(parentId);
+                        result.tasks.get(parentId).inPorts.put(id, inPort);
+                    } else if (result.operations.containsKey(parentId)) {
+                        inPort.parentOperation = result.operations.get(parentId);
+                        result.operations.get(parentId).inPorts.put(id, inPort);
+                    } else {
+                        throw new Exception("Failed to find parent id " + parentId + " for in port " + id);
+                    }
+
+                } else if ("OutPort".equals(eElement.getTagName())) {
+
+                    int id = Integer.parseInt(eElement.getAttribute("id"));
+                    String name = eElement.getAttribute("name");
+
+                    NamedNodeMap attributes = eElement.getAttributes();
+                    Element mxCell = null;
+
+                    NodeList childNodes = eElement.getChildNodes();
+                    for (int i = 0 ; i < childNodes.getLength(); i++) {
+                        Node tempOutPort = childNodes.item(i);
+                        if (tempOutPort.getNodeType() == Node.ELEMENT_NODE) {
+                            Element tmpOutElement = (Element) tempOutPort;
+                            if ("mxCell".equals(tmpOutElement.getTagName())) {
+                                mxCell = tmpOutElement;
+                                break;
+                            }
+                        }
+                    }
+
+                    int parentId = Integer.parseInt(mxCell.getAttribute("parent"));
+
+                    OutPort outPort = new OutPort();
+                    outPort.id = id;
+                    outPort.name = name;
+                    result.portCache.put(id, outPort);
+
+                    if (result.tasks.containsKey(parentId)) {
+                        outPort.parentTask = result.tasks.get(parentId);
+                        result.tasks.get(parentId).outPorts.put(id, outPort);
+                        result.tasks.get(parentId).getTaskResource().getOutPorts()
+                                .add(new TaskOutPortResource()
+                                        .setName(outPort.name)
+                                        .setReferenceId(outPort.id));
+
+                    } else if (result.operations.containsKey(parentId)) {
+                        outPort.parentOperation = result.operations.get(parentId);
+                        result.operations.get(parentId).outPorts.put(id, outPort);
+                    } else {
+                        throw new Exception("Failed to find parent id " + parentId + " for out port " + id);
+                    }
+
+                } else if ("mxCell".equals(eElement.getTagName())) {
+                    if (eElement.hasAttribute("edge") && eElement.hasAttribute("source") && eElement.hasAttribute("target")) {
+                        int sourceId = Integer.parseInt(eElement.getAttribute("source"));
+                        int targetId = Integer.parseInt(eElement.getAttribute("target"));
+
+                        if (result.portCache.containsKey(targetId) && result.portCache.containsKey(sourceId)) {
+                            InPort inPort = (InPort) result.portCache.get(targetId);
+                            OutPort outPort = (OutPort) result.portCache.get(sourceId);
+                            outPort.nextPort = inPort;
+                            inPort.previousPorts.put(outPort.id, outPort);
+                            result.edgeCache.put(outPort, inPort);
+                        } else {
+                            throw new Exception("Invalid connection with source id " + sourceId + " target id " + targetId);
+                        }
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+
+    public static class ParseResult {
+
+
+        private Map<Integer, Task> tasks = new HashMap<>();
+        private Map<Integer, Operation> operations = new HashMap<>();
+        private Map<Integer, Object> portCache = new HashMap<>();
+        private Map<OutPort, InPort> edgeCache = new HashMap<>();
+
+        public Map<Integer, Task> getTasks() {
+            return tasks;
+        }
+
+        public ParseResult setTasks(Map<Integer, Task> tasks) {
+            this.tasks = tasks;
+            return this;
+        }
+
+        public Map<Integer, Operation> getOperations() {
+            return operations;
+        }
+
+        public ParseResult setOperations(Map<Integer, Operation> operations) {
+            this.operations = operations;
+            return this;
+        }
+
+        public Map<Integer, Object> getPortCache() {
+            return portCache;
+        }
+
+        public ParseResult setPortCache(Map<Integer, Object> portCache) {
+            this.portCache = portCache;
+            return this;
+        }
+
+        public Map<OutPort, InPort> getEdgeCache() {
+            return edgeCache;
+        }
+
+        public ParseResult setEdgeCache(Map<OutPort, InPort> edgeCache) {
+            this.edgeCache = edgeCache;
+            return this;
+        }
+    }
+
+
+    public static class Task {
+        private int id;
+        private TaskResource taskResource;
+        private Map<Integer, InPort> inPorts = new HashMap<>();
+        private Map<Integer, OutPort> outPorts = new HashMap<>();
+
+        public int getId() {
+            return id;
+        }
+
+        public Task setId(int id) {
+            this.id = id;
+            return this;
+        }
+
+        public TaskResource getTaskResource() {
+            return taskResource;
+        }
+
+        public Task setTaskResource(TaskResource taskResource) {
+            this.taskResource = taskResource;
+            return this;
+        }
+
+        public Map<Integer, InPort> getInPorts() {
+            return inPorts;
+        }
+
+        public Task setInPorts(Map<Integer, InPort> inPorts) {
+            this.inPorts = inPorts;
+            return this;
+        }
+
+        public Map<Integer, OutPort> getOutPorts() {
+            return outPorts;
+        }
+
+        public Task setOutPorts(Map<Integer, OutPort> outPorts) {
+            this.outPorts = outPorts;
+            return this;
+        }
+    }
+
+    public static class OutPort {
+        private InPort nextPort;
+        private Task parentTask;
+        private Operation parentOperation;
+        private String name;
+        private int id;
+
+        public InPort getNextPort() {
+            return nextPort;
+        }
+
+        public OutPort setNextPort(InPort nextPort) {
+            this.nextPort = nextPort;
+            return this;
+        }
+
+        public Task getParentTask() {
+            return parentTask;
+        }
+
+        public OutPort setParentTask(Task parentTask) {
+            this.parentTask = parentTask;
+            return this;
+        }
+
+        public Operation getParentOperation() {
+            return parentOperation;
+        }
+
+        public OutPort setParentOperation(Operation parentOperation) {
+            this.parentOperation = parentOperation;
+            return this;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public OutPort setName(String name) {
+            this.name = name;
+            return this;
+        }
+
+        public int getId() {
+            return id;
+        }
+
+        public OutPort setId(int id) {
+            this.id = id;
+            return this;
+        }
+    }
+
+    public static class InPort {
+        private Map<Integer, OutPort> previousPorts = new HashMap<>();
+        private Task parentTask;
+        private Operation parentOperation;
+        private String name;
+        private int id;
+
+        public Map<Integer, OutPort> getPreviousPorts() {
+            return previousPorts;
+        }
+
+        public InPort setPreviousPorts(Map<Integer, OutPort> previousPorts) {
+            this.previousPorts = previousPorts;
+            return this;
+        }
+
+        public Task getParentTask() {
+            return parentTask;
+        }
+
+        public InPort setParentTask(Task parentTask) {
+            this.parentTask = parentTask;
+            return this;
+        }
+
+        public Operation getParentOperation() {
+            return parentOperation;
+        }
+
+        public InPort setParentOperation(Operation parentOperation) {
+            this.parentOperation = parentOperation;
+            return this;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public InPort setName(String name) {
+            this.name = name;
+            return this;
+        }
+
+        public int getId() {
+            return id;
+        }
+
+        public InPort setId(int id) {
+            this.id = id;
+            return this;
+        }
+    }
+
+    public static class Operation {
+        private int id;
+        private String name;
+        private Map<Integer, InPort> inPorts = new HashMap<>();
+        private Map<Integer, OutPort> outPorts = new HashMap<>();
+
+        public int getId() {
+            return id;
+        }
+
+        public Operation setId(int id) {
+            this.id = id;
+            return this;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public Operation setName(String name) {
+            this.name = name;
+            return this;
+        }
+
+        public Map<Integer, InPort> getInPorts() {
+            return inPorts;
+        }
+
+        public Operation setInPorts(Map<Integer, InPort> inPorts) {
+            this.inPorts = inPorts;
+            return this;
+        }
+
+        public Map<Integer, OutPort> getOutPorts() {
+            return outPorts;
+        }
+
+        public Operation setOutPorts(Map<Integer, OutPort> outPorts) {
+            this.outPorts = outPorts;
+            return this;
+        }
+    }
+
+}
diff --git a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/util/ToResourceUtil.java b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/util/ToResourceUtil.java
index 0318978..7631b01 100644
--- a/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/util/ToResourceUtil.java
+++ b/airavata-kubernetes/modules/microservices/api-server/src/main/java/org/apache/airavata/k8s/api/server/service/util/ToResourceUtil.java
@@ -21,9 +21,12 @@ package org.apache.airavata.k8s.api.server.service.util;
 
 import org.apache.airavata.k8s.api.resources.experiment.ExperimentStatusResource;
 import org.apache.airavata.k8s.api.resources.process.ProcessStatusResource;
-import org.apache.airavata.k8s.api.resources.task.TaskParamResource;
-import org.apache.airavata.k8s.api.resources.task.TaskResource;
-import org.apache.airavata.k8s.api.resources.task.TaskStatusResource;
+import org.apache.airavata.k8s.api.resources.task.*;
+import org.apache.airavata.k8s.api.resources.task.type.TaskInputTypeResource;
+import org.apache.airavata.k8s.api.resources.task.type.TaskOutPortTypeResource;
+import org.apache.airavata.k8s.api.resources.task.type.TaskOutputTypeResource;
+import org.apache.airavata.k8s.api.resources.task.type.TaskTypeResource;
+import org.apache.airavata.k8s.api.resources.workflow.WorkflowResource;
 import org.apache.airavata.k8s.api.server.model.application.ApplicationDeployment;
 import org.apache.airavata.k8s.api.server.model.application.ApplicationInterface;
 import org.apache.airavata.k8s.api.server.model.application.ApplicationModule;
@@ -34,6 +37,7 @@ import org.apache.airavata.k8s.api.server.model.experiment.ExperimentOutputData;
 import org.apache.airavata.k8s.api.server.model.experiment.ExperimentStatus;
 import org.apache.airavata.k8s.api.server.model.process.ProcessModel;
 import org.apache.airavata.k8s.api.server.model.process.ProcessStatus;
+import org.apache.airavata.k8s.api.server.model.task.TaskInput;
 import org.apache.airavata.k8s.api.server.model.task.TaskModel;
 import org.apache.airavata.k8s.api.resources.application.*;
 import org.apache.airavata.k8s.api.resources.compute.ComputeResource;
@@ -41,8 +45,13 @@ import org.apache.airavata.k8s.api.resources.experiment.ExperimentInputResource;
 import org.apache.airavata.k8s.api.resources.experiment.ExperimentOutputResource;
 import org.apache.airavata.k8s.api.resources.experiment.ExperimentResource;
 import org.apache.airavata.k8s.api.resources.process.ProcessResource;
-import org.apache.airavata.k8s.api.server.model.task.TaskParam;
+import org.apache.airavata.k8s.api.server.model.task.TaskOutput;
 import org.apache.airavata.k8s.api.server.model.task.TaskStatus;
+import org.apache.airavata.k8s.api.server.model.task.type.TaskInputType;
+import org.apache.airavata.k8s.api.server.model.task.type.TaskModelType;
+import org.apache.airavata.k8s.api.server.model.task.type.TaskOutPortType;
+import org.apache.airavata.k8s.api.server.model.task.type.TaskOutputType;
+import org.apache.airavata.k8s.api.server.model.workflow.Workflow;
 
 import java.util.Optional;
 
@@ -217,17 +226,25 @@ public class ToResourceUtil {
     public static Optional<TaskResource> toResource(TaskModel taskModel) {
         if (taskModel != null) {
             TaskResource resource = new TaskResource();
+            resource.setName(taskModel.getName());
             resource.setId(taskModel.getId());
             resource.setLastUpdateTime(taskModel.getLastUpdateTime());
             resource.setCreationTime(taskModel.getCreationTime());
             resource.setParentProcessId(taskModel.getParentProcess().getId());
-            resource.setTaskType(taskModel.getTaskType().getValue());
-            resource.setTaskTypeStr(taskModel.getTaskType().name());
+            resource.setTaskTypeId(taskModel.getTaskType().getId());
             resource.setTaskDetail(taskModel.getTaskDetail());
-            Optional.ofNullable(taskModel.getTaskParams())
-                    .ifPresent(params ->
-                            params.forEach(param -> resource.getTaskParams()
-                                    .add(toResource(param).get())));
+            resource.setStartingTask(taskModel.isStartingTask());
+            resource.setStoppingTask(taskModel.isStoppingTask());
+            Optional.ofNullable(taskModel.getTaskInputs())
+                    .ifPresent(inputs ->
+                            inputs.forEach(input -> resource.getInputs()
+                                    .add(toResource(input).get())));
+
+            Optional.ofNullable(taskModel.getTaskOutputs())
+                    .ifPresent(outputs ->
+                            outputs.forEach(output -> resource.getOutputs()
+                                    .add(toResource(output).get())));
+
 
             Optional.ofNullable(taskModel.getTaskStatuses())
                     .ifPresent(taskStatuses ->
@@ -256,12 +273,28 @@ public class ToResourceUtil {
         }
     }
 
-    public static Optional<TaskParamResource> toResource(TaskParam taskParam) {
-        if (taskParam != null) {
-            TaskParamResource resource = new TaskParamResource();
-            resource.setId(taskParam.getId());
-            resource.setKey(taskParam.getKey());
-            resource.setValue(taskParam.getValue());
+    public static Optional<TaskInputResource> toResource(TaskInput taskInput) {
+        if (taskInput != null) {
+            TaskInputResource resource = new TaskInputResource();
+            resource.setId(taskInput.getId());
+            resource.setName(taskInput.getName());
+            resource.setValue(taskInput.getValue());
+            resource.setType(taskInput.getType());
+            resource.setImportFrom(taskInput.getImportFrom());
+            return Optional.of(resource);
+        } else {
+            return Optional.empty();
+        }
+    }
+
+    public static Optional<TaskOutputResource> toResource(TaskOutput taskOutput) {
+        if (taskOutput != null) {
+            TaskOutputResource resource = new TaskOutputResource();
+            resource.setId(taskOutput.getId());
+            resource.setName(taskOutput.getName());
+            resource.setValue(taskOutput.getValue());
+            resource.setType(taskOutput.getType());
+            resource.setExportTo(taskOutput.getExportTo());
             return Optional.of(resource);
         } else {
             return Optional.empty();
@@ -302,4 +335,87 @@ public class ToResourceUtil {
             return Optional.empty();
         }
     }
+
+    public static Optional<TaskTypeResource> toResource(TaskModelType taskModelType) {
+
+        if (taskModelType != null) {
+            TaskTypeResource resource = new TaskTypeResource();
+            resource.setName(taskModelType.getName());
+            resource.setId(taskModelType.getId());
+            resource.setTopicName(taskModelType.getTopicName());
+            resource.setIcon(taskModelType.getIcon());
+
+            Optional.ofNullable(taskModelType.getInputTypes())
+                    .ifPresent(taskInputTypes -> taskInputTypes.forEach(taskInputType -> {
+                        resource.getInputTypes().add(toResource(taskInputType).get());
+                    }));
+
+            Optional.ofNullable(taskModelType.getOutputTypes())
+                    .ifPresent(taskOutputTypes -> taskOutputTypes.forEach(taskOutputType -> {
+                        resource.getOutputTypes().add(toResource(taskOutputType).get());
+                    }));
+
+            Optional.ofNullable(taskModelType.getOutPorts())
+                    .ifPresent(taskOutPorts -> taskOutPorts.forEach(taskOutPort -> {
+                        resource.getOutPorts().add(toResource(taskOutPort).get());
+                    }));
+            return Optional.of(resource);
+        } else {
+            return Optional.empty();
+        }
+    }
+
+    public static Optional<TaskInputTypeResource> toResource(TaskInputType taskInputType) {
+        if (taskInputType != null) {
+            TaskInputTypeResource resource = new TaskInputTypeResource();
+            resource.setId(taskInputType.getId());
+            resource.setName(taskInputType.getName());
+            resource.setType(taskInputType.getType());
+            resource.setDefaultValue(taskInputType.getDefaultValue());
+            return Optional.of(resource);
+        } else {
+            return Optional.empty();
+        }
+    }
+
+    public static Optional<TaskOutputTypeResource> toResource(TaskOutputType taskOutputType) {
+        if (taskOutputType != null) {
+            TaskOutputTypeResource resource = new TaskOutputTypeResource();
+            resource.setId(taskOutputType.getId());
+            resource.setName(taskOutputType.getName());
+            resource.setType(taskOutputType.getType());
+            return Optional.of(resource);
+        } else {
+            return Optional.empty();
+        }
+    }
+
+    public static Optional<TaskOutPortTypeResource> toResource(TaskOutPortType taskOutPort) {
+        if (taskOutPort != null) {
+            TaskOutPortTypeResource resource = new TaskOutPortTypeResource();
+            resource.setId(taskOutPort.getId());
+            resource.setName(taskOutPort.getName());
+            resource.setOrder(taskOutPort.getOrder());
+            return Optional.of(resource);
+        } else {
+            return Optional.empty();
+        }
+    }
+
+    public static Optional<WorkflowResource> toResource(Workflow workflow) {
+        if (workflow != null) {
+            WorkflowResource resource = new WorkflowResource();
+            resource.setId(workflow.getId());
+            resource.setName(workflow.getName());
+            resource.setWorkflowGraphXML(new String(workflow.getWorkFlowGraph()));
+            Optional.ofNullable(workflow.getProcesses()).ifPresent(processModels -> {
+                processModels.forEach(processModel -> {
+                    resource.getProcessIds().add(processModel.getId());
+                });
+            });
+            return Optional.of(resource);
+        } else {
+            return Optional.empty();
+        }
+    }
 }
diff --git a/airavata-kubernetes/modules/microservices/task-scheduler/src/main/java/org/apache/airavata/k8s/gfac/core/ProcessLifeCycleManager.java b/airavata-kubernetes/modules/microservices/task-scheduler/src/main/java/org/apache/airavata/k8s/gfac/core/ProcessLifeCycleManager.java
index 01e53a2..751b2ec 100644
--- a/airavata-kubernetes/modules/microservices/task-scheduler/src/main/java/org/apache/airavata/k8s/gfac/core/ProcessLifeCycleManager.java
+++ b/airavata-kubernetes/modules/microservices/task-scheduler/src/main/java/org/apache/airavata/k8s/gfac/core/ProcessLifeCycleManager.java
@@ -84,33 +84,6 @@ public class ProcessLifeCycleManager {
 
     public void submitTaskToQueue(TaskResource taskResource) {
 
-        switch (taskResource.getTaskType()) {
-            case TaskResource.TaskTypes.EGRESS_DATA_STAGING :
-                System.out.println("Submitting task " + taskResource.getId() + " to egress data staging queue");
-                updateProcessStatus(ProcessStatusResource.State.OUTPUT_DATA_STAGING);
-                this.kafkaSender.send("airavata-task-egress-staging", taskResource.getId() + "");
-                break;
-            case TaskResource.TaskTypes.INGRESS_DATA_STAGING :
-                System.out.println("Submitting task " + taskResource.getId() + " to ingress data staging queue");
-                updateProcessStatus(ProcessStatusResource.State.INPUT_DATA_STAGING);
-                this.kafkaSender.send("airavata-task-ingress-staging", taskResource.getId() + "");
-                break;
-            case TaskResource.TaskTypes.ENV_SETUP :
-                System.out.println("Submitting task " + taskResource.getId() + " to env setup queue");
-                updateProcessStatus(ProcessStatusResource.State.PRE_PROCESSING);
-                this.kafkaSender.send("airavata-task-env-setup", taskResource.getId() + "");
-                break;
-            case TaskResource.TaskTypes.ENV_CLEANUP :
-                System.out.println("Submitting task " + taskResource.getId() + " to env cleanup queue");
-                updateProcessStatus(ProcessStatusResource.State.POST_PROCESSING);
-                this.kafkaSender.send("airavata-task-env-cleanup", taskResource.getId() + "");
-                break;
-            case TaskResource.TaskTypes.JOB_SUBMISSION :
-                System.out.println("Submitting task " + taskResource.getId() + " to job submission queue");
-                updateProcessStatus(ProcessStatusResource.State.EXECUTING);
-                this.kafkaSender.send("airavata-task-job-submission", taskResource.getId() + "");
-                break;
-        }
     }
 
     private void updateProcessStatus(int state) {
diff --git a/airavata-kubernetes/modules/microservices/tasks/job-submission-task/pom.xml b/airavata-kubernetes/modules/microservices/tasks/command-executing-task/pom.xml
similarity index 96%
rename from airavata-kubernetes/modules/microservices/tasks/job-submission-task/pom.xml
rename to airavata-kubernetes/modules/microservices/tasks/command-executing-task/pom.xml
index c046b87..eff73f5 100644
--- a/airavata-kubernetes/modules/microservices/tasks/job-submission-task/pom.xml
+++ b/airavata-kubernetes/modules/microservices/tasks/command-executing-task/pom.xml
@@ -31,7 +31,7 @@
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
-    <artifactId>job-submission-task</artifactId>
+    <artifactId>command-executing-task</artifactId>
 
     <dependencies>
         <dependency>
@@ -44,6 +44,11 @@
             <artifactId>api-resource</artifactId>
             <version>1.0-SNAPSHOT</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.airavata</groupId>
+            <artifactId>task-api</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
 
         <dependency>
             <groupId>org.springframework.boot</groupId>
diff --git a/airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/java/org/apache/airavata/k8s/task/job/Application.java b/airavata-kubernetes/modules/microservices/tasks/command-executing-task/src/main/java/org/apache/airavata/k8s/task/job/Application.java
similarity index 81%
rename from airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/java/org/apache/airavata/k8s/task/job/Application.java
rename to airavata-kubernetes/modules/microservices/tasks/command-executing-task/src/main/java/org/apache/airavata/k8s/task/job/Application.java
index 9955502..d11a7c5 100644
--- a/airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/java/org/apache/airavata/k8s/task/job/Application.java
+++ b/airavata-kubernetes/modules/microservices/tasks/command-executing-task/src/main/java/org/apache/airavata/k8s/task/job/Application.java
@@ -19,12 +19,16 @@
  */
 package org.apache.airavata.k8s.task.job;
 
+import org.apache.airavata.k8s.task.api.messaging.ReceiverConfig;
+import org.apache.airavata.k8s.task.api.messaging.SenderConfig;
 import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.web.client.RestTemplateBuilder;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
 import org.springframework.web.client.RestTemplate;
 
 /**
@@ -35,7 +39,9 @@ import org.springframework.web.client.RestTemplate;
  */
 @SpringBootApplication
 @Configuration
-@ComponentScan
+@ComponentScan(basePackages = "org.apache.airavata.*")
+@EnableAutoConfiguration
+@Import({ReceiverConfig.class, SenderConfig.class})
 public class Application {
 
     public static void main(String[] args) {
diff --git a/airavata-kubernetes/modules/microservices/tasks/command-executing-task/src/main/java/org/apache/airavata/k8s/task/job/CommandTaskInfo.java b/airavata-kubernetes/modules/microservices/tasks/command-executing-task/src/main/java/org/apache/airavata/k8s/task/job/CommandTaskInfo.java
new file mode 100644
index 0000000..cbffce3
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/tasks/command-executing-task/src/main/java/org/apache/airavata/k8s/task/job/CommandTaskInfo.java
@@ -0,0 +1,59 @@
+package org.apache.airavata.k8s.task.job;
+
+import org.apache.airavata.k8s.api.resources.task.type.TaskInputTypeResource;
+import org.apache.airavata.k8s.api.resources.task.type.TaskOutPortTypeResource;
+import org.apache.airavata.k8s.api.resources.task.type.TaskTypeResource;
+
+import java.util.Arrays;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public class CommandTaskInfo {
+
+    public static final String COMMAND = "command";
+    public static final String ARGUMENTS = "arguments";
+    public static final String STD_OUT_PATH = "std_out_path";
+    public static final String STD_ERR_PATH = "std_err_path";
+    public static final String COMPUTE_RESOURCE = "compute_resource";
+
+    public static TaskTypeResource getTaskType() {
+        TaskTypeResource taskTypeResource = new TaskTypeResource();
+        taskTypeResource.setName("Command Execute");
+        taskTypeResource.setTopicName("airavata-command");
+        taskTypeResource.setIcon("assets/icons/ssh.png");
+        taskTypeResource.getInputTypes().addAll(
+                Arrays.asList(
+                        new TaskInputTypeResource()
+                                .setName(COMMAND)
+                                .setType("String")
+                                .setDefaultValue(""),
+                        new TaskInputTypeResource()
+                                .setName(ARGUMENTS)
+                                .setType("String"),
+                        new TaskInputTypeResource()
+                                .setName(COMPUTE_RESOURCE)
+                                .setType("Long"),
+                        new TaskInputTypeResource()
+                                .setName(STD_OUT_PATH)
+                                .setType("String"),
+                        new TaskInputTypeResource()
+                                .setName(STD_ERR_PATH)
+                                .setType("String")));
+
+        taskTypeResource.getOutPorts().addAll(
+                Arrays.asList(
+                        new TaskOutPortTypeResource()
+                                .setName("Out")
+                                .setOrder(0),
+                        new TaskOutPortTypeResource()
+                                .setName("Error")
+                                .setOrder(1))
+        );
+
+        return taskTypeResource;
+    }
+}
diff --git a/airavata-kubernetes/modules/microservices/tasks/command-executing-task/src/main/java/org/apache/airavata/k8s/task/job/config/RestConfig.java b/airavata-kubernetes/modules/microservices/tasks/command-executing-task/src/main/java/org/apache/airavata/k8s/task/job/config/RestConfig.java
new file mode 100644
index 0000000..acbf74f
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/tasks/command-executing-task/src/main/java/org/apache/airavata/k8s/task/job/config/RestConfig.java
@@ -0,0 +1,10 @@
+package org.apache.airavata.k8s.task.job.config;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public class RestConfig {
+}
diff --git a/airavata-kubernetes/modules/microservices/tasks/command-executing-task/src/main/java/org/apache/airavata/k8s/task/job/service/TaskExecutionService.java b/airavata-kubernetes/modules/microservices/tasks/command-executing-task/src/main/java/org/apache/airavata/k8s/task/job/service/TaskExecutionService.java
new file mode 100644
index 0000000..23c147e
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/tasks/command-executing-task/src/main/java/org/apache/airavata/k8s/task/job/service/TaskExecutionService.java
@@ -0,0 +1,100 @@
+/**
+ *
+ * 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.airavata.k8s.task.job.service;
+
+import org.apache.airavata.k8s.api.resources.compute.ComputeResource;
+import org.apache.airavata.k8s.api.resources.task.TaskResource;
+import org.apache.airavata.k8s.api.resources.task.TaskStatusResource;
+import org.apache.airavata.k8s.api.resources.task.type.TaskTypeResource;
+import org.apache.airavata.k8s.compute.api.ExecutionResult;
+import org.apache.airavata.k8s.task.api.AbstractTaskExecutionService;
+import org.apache.airavata.k8s.task.api.TaskContext;
+import org.apache.airavata.k8s.task.api.messaging.KafkaSender;
+import org.apache.airavata.k8s.task.job.CommandTaskInfo;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+@Service
+public class TaskExecutionService extends AbstractTaskExecutionService {
+
+    public TaskExecutionService(RestTemplate restTemplate, KafkaSender kafkaSender) {
+        super(restTemplate, null, 10);
+    }
+
+    @Override
+    public TaskTypeResource getType() {
+        return CommandTaskInfo.getTaskType();
+    }
+
+    @Override
+    public void initializeParameters(TaskResource taskResource, TaskContext taskContext) throws Exception {
+
+        taskContext.getLocalContext().put(CommandTaskInfo.COMMAND, findInput(taskResource, CommandTaskInfo.COMMAND, false));
+        taskContext.getLocalContext().put(CommandTaskInfo.ARGUMENTS, findInput(taskResource, CommandTaskInfo.ARGUMENTS, true));
+        taskContext.getLocalContext().put(CommandTaskInfo.STD_OUT_PATH, findInput(taskResource, CommandTaskInfo.STD_OUT_PATH, false));
+        taskContext.getLocalContext().put(CommandTaskInfo.STD_ERR_PATH, findInput(taskResource, CommandTaskInfo.STD_ERR_PATH, false));
+
+        String computeId = findInput(taskResource, CommandTaskInfo.COMPUTE_RESOURCE, false);
+        taskContext.getLocalContext().put(CommandTaskInfo.COMPUTE_RESOURCE, this.getRestTemplate().getForObject("http://" + this.getApiServerUrl()
+                + "/compute/" + Long.parseLong(computeId), ComputeResource.class));
+
+    }
+
+    @Override
+    public void executeTask(TaskResource taskResource, TaskContext taskContext) {
+
+        try {
+            String command = (String) taskContext.getLocalContext().get(CommandTaskInfo.COMMAND);
+            String arguments = (String) taskContext.getLocalContext().get(CommandTaskInfo.ARGUMENTS);
+            ComputeResource computeResource = (ComputeResource) taskContext.getLocalContext().get(CommandTaskInfo.COMPUTE_RESOURCE);
+            String stdOutPath = (String) taskContext.getLocalContext().get(CommandTaskInfo.STD_OUT_PATH);
+            String stdErrPath = (String) taskContext.getLocalContext().get(CommandTaskInfo.STD_ERR_PATH);
+            String stdOutSuffix = " > " + stdOutPath + " 2> " + stdErrPath;
+
+            publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.EXECUTING);
+
+            String finalCommand = command + (arguments != null ? arguments : "") + stdOutSuffix;
+
+            System.out.println("Executing command " + finalCommand);
+
+            ExecutionResult executionResult = fetchComputeResourceOperation(computeResource).executeCommand(finalCommand);
+
+            if (executionResult.getExitStatus() == 0) {
+                publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.COMPLETED);
+            } else if (executionResult.getExitStatus() == -1) {
+                publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED, "Process didn't exit successfully");
+            } else {
+                publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED, "Process exited with error status " + executionResult.getExitStatus());
+            }
+
+        } catch (Exception e) {
+
+            e.printStackTrace();
+            publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED, e.getMessage());
+        }
+    }
+}
diff --git a/airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/resources/application.properties b/airavata-kubernetes/modules/microservices/tasks/command-executing-task/src/main/resources/application.properties
similarity index 100%
rename from airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/resources/application.properties
rename to airavata-kubernetes/modules/microservices/tasks/command-executing-task/src/main/resources/application.properties
diff --git a/airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/resources/application.yml b/airavata-kubernetes/modules/microservices/tasks/command-executing-task/src/main/resources/application.yml
similarity index 100%
rename from airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/resources/application.yml
rename to airavata-kubernetes/modules/microservices/tasks/command-executing-task/src/main/resources/application.yml
diff --git a/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/pom.xml b/airavata-kubernetes/modules/microservices/tasks/data-collecting-task/pom.xml
similarity index 96%
rename from airavata-kubernetes/modules/microservices/tasks/egress-staging-task/pom.xml
rename to airavata-kubernetes/modules/microservices/tasks/data-collecting-task/pom.xml
index 05852fe..fe50fe8 100644
--- a/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/pom.xml
+++ b/airavata-kubernetes/modules/microservices/tasks/data-collecting-task/pom.xml
@@ -31,7 +31,7 @@
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
-    <artifactId>egress-staging-task</artifactId>
+    <artifactId>data-collecting-task</artifactId>
 
     <dependencies>
         <dependency>
@@ -44,6 +44,11 @@
             <artifactId>api-resource</artifactId>
             <version>1.0-SNAPSHOT</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.airavata</groupId>
+            <artifactId>task-api</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
 
         <dependency>
             <groupId>org.springframework.boot</groupId>
diff --git a/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/java/org/apacher/airavata/k8s/task/egress/Application.java b/airavata-kubernetes/modules/microservices/tasks/data-collecting-task/src/main/java/org/apacher/airavata/k8s/task/egress/Application.java
similarity index 83%
rename from airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/java/org/apacher/airavata/k8s/task/egress/Application.java
rename to airavata-kubernetes/modules/microservices/tasks/data-collecting-task/src/main/java/org/apacher/airavata/k8s/task/egress/Application.java
index e2dbae4..1642d18 100644
--- a/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/java/org/apacher/airavata/k8s/task/egress/Application.java
+++ b/airavata-kubernetes/modules/microservices/tasks/data-collecting-task/src/main/java/org/apacher/airavata/k8s/task/egress/Application.java
@@ -19,12 +19,16 @@
  */
 package org.apacher.airavata.k8s.task.egress;
 
+import org.apache.airavata.k8s.task.api.messaging.ReceiverConfig;
+import org.apache.airavata.k8s.task.api.messaging.SenderConfig;
 import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.web.client.RestTemplateBuilder;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
 import org.springframework.web.client.RestTemplate;
 
 /**
@@ -36,6 +40,8 @@ import org.springframework.web.client.RestTemplate;
 @SpringBootApplication
 @Configuration
 @ComponentScan
+@EnableAutoConfiguration
+@Import({ReceiverConfig.class, SenderConfig.class})
 public class Application {
 
     public static void main(String[] args) {
diff --git a/airavata-kubernetes/modules/microservices/tasks/data-collecting-task/src/main/java/org/apacher/airavata/k8s/task/egress/DataCollectingTaskInfo.java b/airavata-kubernetes/modules/microservices/tasks/data-collecting-task/src/main/java/org/apacher/airavata/k8s/task/egress/DataCollectingTaskInfo.java
new file mode 100644
index 0000000..73a354f
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/tasks/data-collecting-task/src/main/java/org/apacher/airavata/k8s/task/egress/DataCollectingTaskInfo.java
@@ -0,0 +1,50 @@
+package org.apacher.airavata.k8s.task.egress;
+
+import org.apache.airavata.k8s.api.resources.task.type.TaskInputTypeResource;
+import org.apache.airavata.k8s.api.resources.task.type.TaskOutPortTypeResource;
+import org.apache.airavata.k8s.api.resources.task.type.TaskTypeResource;
+
+import java.util.Arrays;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public class DataCollectingTaskInfo {
+    public static final String REMOTE_SOURCE_PATH = "remote_source_path";
+    public static final String COMPUTE_RESOURCE = "compute_resource";
+    public static final String IDENTIFIER = "identifier";
+
+    public static TaskTypeResource getTaskType() {
+        TaskTypeResource taskTypeResource = new TaskTypeResource();
+        taskTypeResource.setName("Data Collect");
+        taskTypeResource.setTopicName("airavata-data-collect");
+        taskTypeResource.setIcon("assets/icons/copy.png");
+        taskTypeResource.getInputTypes().addAll(
+                Arrays.asList(
+                        new TaskInputTypeResource()
+                                .setName(REMOTE_SOURCE_PATH)
+                                .setType("String")
+                                .setDefaultValue(""),
+                        new TaskInputTypeResource()
+                                .setName(IDENTIFIER)
+                                .setType("String"),
+                        new TaskInputTypeResource()
+                                .setName(COMPUTE_RESOURCE)
+                                .setType("Long")));
+
+        taskTypeResource.getOutPorts().addAll(
+                Arrays.asList(
+                        new TaskOutPortTypeResource()
+                                .setName("Out")
+                                .setOrder(0),
+                        new TaskOutPortTypeResource()
+                                .setName("Error")
+                                .setOrder(1))
+        );
+
+        return taskTypeResource;
+    }
+}
diff --git a/airavata-kubernetes/modules/microservices/tasks/data-collecting-task/src/main/java/org/apacher/airavata/k8s/task/egress/service/TaskExecutionService.java b/airavata-kubernetes/modules/microservices/tasks/data-collecting-task/src/main/java/org/apacher/airavata/k8s/task/egress/service/TaskExecutionService.java
new file mode 100644
index 0000000..8b83708
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/tasks/data-collecting-task/src/main/java/org/apacher/airavata/k8s/task/egress/service/TaskExecutionService.java
@@ -0,0 +1,107 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apacher.airavata.k8s.task.egress.service;
+
+import org.apache.airavata.k8s.api.resources.compute.ComputeResource;
+import org.apache.airavata.k8s.api.resources.task.TaskResource;
+import org.apache.airavata.k8s.api.resources.task.TaskStatusResource;
+import org.apache.airavata.k8s.api.resources.task.type.TaskTypeResource;
+import org.apache.airavata.k8s.task.api.AbstractTaskExecutionService;
+import org.apache.airavata.k8s.task.api.TaskContext;
+import org.apache.airavata.k8s.task.api.messaging.KafkaSender;
+import org.apacher.airavata.k8s.task.egress.DataCollectingTaskInfo;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.http.*;
+import org.springframework.stereotype.Service;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.UUID;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+@Service
+public class TaskExecutionService extends AbstractTaskExecutionService {
+
+    public TaskExecutionService(RestTemplate restTemplate, KafkaSender kafkaSender) {
+        super(restTemplate, kafkaSender, 10);
+    }
+
+    @Override
+    public TaskTypeResource getType() {
+        return DataCollectingTaskInfo.getTaskType();
+    }
+
+    @Override
+    public void initializeParameters(TaskResource taskResource, TaskContext taskContext) throws Exception {
+
+        taskContext.getLocalContext().put(DataCollectingTaskInfo.REMOTE_SOURCE_PATH, findInput(taskResource, DataCollectingTaskInfo.REMOTE_SOURCE_PATH, false));
+        taskContext.getLocalContext().put(DataCollectingTaskInfo.IDENTIFIER, findInput(taskResource, DataCollectingTaskInfo.IDENTIFIER, false));
+
+        String computeId = findInput(taskResource, DataCollectingTaskInfo.COMPUTE_RESOURCE, false);
+        taskContext.getLocalContext().put(DataCollectingTaskInfo.COMPUTE_RESOURCE, this.getRestTemplate().getForObject("http://" + this.getApiServerUrl()
+                + "/compute/" + Long.parseLong(computeId), ComputeResource.class));
+
+    }
+
+    public void executeTask(TaskResource taskResource, TaskContext taskContext) {
+
+        try {
+
+            ComputeResource computeResource = (ComputeResource) taskContext.getLocalContext().get(DataCollectingTaskInfo.COMPUTE_RESOURCE);
+            String identifier = (String) taskContext.getLocalContext().get(DataCollectingTaskInfo.IDENTIFIER);
+            String remoteSourcePath = (String) taskContext.getLocalContext().get(DataCollectingTaskInfo.REMOTE_SOURCE_PATH);
+
+            publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.EXECUTING);
+
+            String temporaryFile = "/tmp/" + UUID.randomUUID().toString();
+            System.out.println("Downloading " + remoteSourcePath + " to " + temporaryFile + " from compute resource "
+                    + computeResource.getName());
+
+            fetchComputeResourceOperation(computeResource).transferDataOut(remoteSourcePath, temporaryFile, "SCP");
+
+            LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
+            map.add("file", new FileSystemResource(temporaryFile));
+            HttpHeaders headers = new HttpHeaders();
+            headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+
+            HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<>(map, headers);
+
+            System.out.println("Uploading data file with task id " + taskResource.getId() + " and identifier "
+                    + identifier + " to data store");
+
+            getRestTemplate().exchange("http://" + getApiServerUrl() + "/data/" + taskResource.getId()+ "/"
+                            + identifier + "/upload", HttpMethod.POST, requestEntity, Long.class);
+
+            publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(),
+                    TaskStatusResource.State.COMPLETED);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED, e.getMessage());
+
+        }
+    }
+
+}
diff --git a/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/resources/application.properties b/airavata-kubernetes/modules/microservices/tasks/data-collecting-task/src/main/resources/application.properties
similarity index 100%
rename from airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/resources/application.properties
rename to airavata-kubernetes/modules/microservices/tasks/data-collecting-task/src/main/resources/application.properties
diff --git a/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/resources/application.yml b/airavata-kubernetes/modules/microservices/tasks/data-collecting-task/src/main/resources/application.yml
similarity index 100%
rename from airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/resources/application.yml
rename to airavata-kubernetes/modules/microservices/tasks/data-collecting-task/src/main/resources/application.yml
diff --git a/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/pom.xml b/airavata-kubernetes/modules/microservices/tasks/data-pushing-task/pom.xml
similarity index 96%
rename from airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/pom.xml
rename to airavata-kubernetes/modules/microservices/tasks/data-pushing-task/pom.xml
index c1ae760..533914f 100644
--- a/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/pom.xml
+++ b/airavata-kubernetes/modules/microservices/tasks/data-pushing-task/pom.xml
@@ -31,7 +31,7 @@
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
-    <artifactId>ingress-staging-task</artifactId>
+    <artifactId>data-pushing-task</artifactId>
 
     <dependencies>
         <dependency>
@@ -44,6 +44,11 @@
             <artifactId>api-resource</artifactId>
             <version>1.0-SNAPSHOT</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.airavata</groupId>
+            <artifactId>task-api</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
 
         <dependency>
             <groupId>org.springframework.boot</groupId>
diff --git a/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/java/org/apache/airavata/k8s/task/ingress/Application.java b/airavata-kubernetes/modules/microservices/tasks/data-pushing-task/src/main/java/org/apache/airavata/k8s/task/ingress/Application.java
similarity index 100%
rename from airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/java/org/apache/airavata/k8s/task/ingress/Application.java
rename to airavata-kubernetes/modules/microservices/tasks/data-pushing-task/src/main/java/org/apache/airavata/k8s/task/ingress/Application.java
diff --git a/airavata-kubernetes/modules/microservices/tasks/data-pushing-task/src/main/java/org/apache/airavata/k8s/task/ingress/service/TaskExecutionService.java b/airavata-kubernetes/modules/microservices/tasks/data-pushing-task/src/main/java/org/apache/airavata/k8s/task/ingress/service/TaskExecutionService.java
new file mode 100644
index 0000000..cb3fada
--- /dev/null
+++ b/airavata-kubernetes/modules/microservices/tasks/data-pushing-task/src/main/java/org/apache/airavata/k8s/task/ingress/service/TaskExecutionService.java
@@ -0,0 +1,83 @@
+/**
+ *
+ * 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.airavata.k8s.task.ingress.service;
+
+import org.apache.airavata.k8s.api.resources.compute.ComputeResource;
+import org.apache.airavata.k8s.api.resources.task.TaskResource;
+import org.apache.airavata.k8s.api.resources.task.TaskStatusResource;
+import org.apache.airavata.k8s.api.resources.task.type.TaskTypeResource;
+import org.apache.airavata.k8s.task.api.AbstractTaskExecutionService;
+import org.apache.airavata.k8s.task.api.TaskContext;
+import org.apache.airavata.k8s.task.api.messaging.KafkaSender;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+@Service
+public class TaskExecutionService extends AbstractTaskExecutionService {
+
+    private final String COMPUTE_RESOURCE =  "compute_resource";
+    private final String REMOTE_TARGET_PATH = "remote_target_path";
+    private final String DATA_LOCATION_ID = "data_location_id";
+
+    public TaskExecutionService(RestTemplate restTemplate, KafkaSender kafkaSender) {
+        super(restTemplate, kafkaSender, 10);
+    }
+
+    @Override
+    public TaskTypeResource getType() {
+        return null;
+    }
+
+    @Override
+    public void initializeParameters(TaskResource taskResource, TaskContext taskContext) throws Exception {
+
+        taskContext.getLocalContext().put(DATA_LOCATION_ID, findInput(taskResource, DATA_LOCATION_ID, false));
+        taskContext.getLocalContext().put(REMOTE_TARGET_PATH, findInput(taskResource, REMOTE_TARGET_PATH, false));
+
+        String computeId = findInput(taskResource, COMPUTE_RESOURCE, false);
+        taskContext.getLocalContext().put(COMPUTE_RESOURCE, this.getRestTemplate().getForObject("http://" + this.getApiServerUrl()
+                + "/compute/" + Long.parseLong(computeId), ComputeResource.class));
+    }
+
+    @Override
+    public void executeTask(TaskResource taskResource, TaskContext taskContext) {
+
+        String remoteTargetPath = (String) taskContext.getLocalContext().get(REMOTE_TARGET_PATH);
+        String dataLocationId = (String) taskContext.getLocalContext().get(DATA_LOCATION_ID);
+        ComputeResource computeResource = (ComputeResource) taskContext.getLocalContext().get(COMPUTE_RESOURCE);
+
+        try {
+            publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.EXECUTING);
+            fetchComputeResourceOperation(computeResource).transferDataIn(dataLocationId, remoteTargetPath, "SCP");
+            publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.COMPLETED);
+
+        } catch (Exception e) {
+
+            e.printStackTrace();
+            publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED);
+        }
+    }
+}
diff --git a/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/resources/application.properties b/airavata-kubernetes/modules/microservices/tasks/data-pushing-task/src/main/resources/application.properties
similarity index 100%
rename from airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/resources/application.properties
rename to airavata-kubernetes/modules/microservices/tasks/data-pushing-task/src/main/resources/application.properties
diff --git a/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/resources/application.yml b/airavata-kubernetes/modules/microservices/tasks/data-pushing-task/src/main/resources/application.yml
similarity index 100%
rename from airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/resources/application.yml
rename to airavata-kubernetes/modules/microservices/tasks/data-pushing-task/src/main/resources/application.yml
diff --git a/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/java/org/apacher/airavata/k8s/task/egress/messaging/KafkaReceiver.java b/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/java/org/apacher/airavata/k8s/task/egress/messaging/KafkaReceiver.java
deleted file mode 100644
index ade0aaf..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/java/org/apacher/airavata/k8s/task/egress/messaging/KafkaReceiver.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- *
- * 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.apacher.airavata.k8s.task.egress.messaging;
-
-import org.apacher.airavata.k8s.task.egress.service.TaskExecutionService;
-import org.springframework.kafka.annotation.KafkaListener;
-import org.springframework.kafka.support.Acknowledgment;
-
-import javax.annotation.Resource;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-public class KafkaReceiver {
-
-    @Resource
-    private TaskExecutionService taskExecutionService;
-
-    @KafkaListener(topics = "${task.read.topic.name}")
-    public void receiveTasks(String payload, Acknowledgment ack) {
-        System.out.println("received task=" + payload);
-        taskExecutionService.executeTaskAsync(Long.parseLong(payload));
-        ack.acknowledge();
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/java/org/apacher/airavata/k8s/task/egress/messaging/KafkaSender.java b/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/java/org/apacher/airavata/k8s/task/egress/messaging/KafkaSender.java
deleted file mode 100644
index 253daa4..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/java/org/apacher/airavata/k8s/task/egress/messaging/KafkaSender.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- *
- * 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.apacher.airavata.k8s.task.egress.messaging;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.kafka.core.KafkaTemplate;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-public class KafkaSender {
-
-    @Autowired
-    private KafkaTemplate<String, String> kafkaTemplate;
-
-    public void send(String topic, String payload) {
-        kafkaTemplate.send(topic, payload);
-    }
-
-    public void send(String topic, String key, String payload) {
-        kafkaTemplate.send(topic, key, payload);
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/java/org/apacher/airavata/k8s/task/egress/messaging/SenderConfig.java b/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/java/org/apacher/airavata/k8s/task/egress/messaging/SenderConfig.java
deleted file mode 100644
index 42674a3..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/java/org/apacher/airavata/k8s/task/egress/messaging/SenderConfig.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- *
- * 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.apacher.airavata.k8s.task.egress.messaging;
-
-import org.apache.kafka.clients.producer.ProducerConfig;
-import org.apache.kafka.common.serialization.StringSerializer;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.kafka.core.DefaultKafkaProducerFactory;
-import org.springframework.kafka.core.KafkaTemplate;
-import org.springframework.kafka.core.ProducerFactory;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-@Configuration
-public class SenderConfig {
-    @Value("${kafka.bootstrap-servers}")
-    private String bootstrapServers;
-
-    @Bean
-    public Map<String, Object> producerConfigs() {
-        Map<String, Object> props = new HashMap<>();
-        // list of host:port pairs used for establishing the initial connections to the Kakfa cluster
-        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
-        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
-        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
-        return props;
-    }
-
-    @Bean
-    public ProducerFactory<String, String> producerFactory() {
-        return new DefaultKafkaProducerFactory<String, String>(producerConfigs());
-    }
-
-    @Bean
-    public KafkaTemplate<String, String> kafkaTemplate() {
-        return new KafkaTemplate<>(producerFactory());
-    }
-
-    @Bean
-    public KafkaSender kafkaSender() {
-        return new KafkaSender();
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/java/org/apacher/airavata/k8s/task/egress/service/TaskExecutionService.java b/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/java/org/apacher/airavata/k8s/task/egress/service/TaskExecutionService.java
deleted file mode 100644
index 30fca3b..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/java/org/apacher/airavata/k8s/task/egress/service/TaskExecutionService.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/**
- *
- * 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.apacher.airavata.k8s.task.egress.service;
-
-import org.apache.airavata.k8s.api.resources.compute.ComputeResource;
-import org.apache.airavata.k8s.api.resources.task.TaskParamResource;
-import org.apache.airavata.k8s.api.resources.task.TaskResource;
-import org.apache.airavata.k8s.api.resources.task.TaskStatusResource;
-import org.apache.airavata.k8s.compute.api.ComputeOperations;
-import org.apache.airavata.k8s.compute.impl.MockComputeOperation;
-import org.apache.airavata.k8s.compute.impl.SSHComputeOperations;
-import org.apacher.airavata.k8s.task.egress.messaging.KafkaSender;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.core.io.FileSystemResource;
-import org.springframework.http.*;
-import org.springframework.stereotype.Service;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.web.client.RestTemplate;
-
-import java.util.Optional;
-import java.util.UUID;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-@Service
-public class TaskExecutionService {
-
-    private final ExecutorService executorService = Executors.newFixedThreadPool(10);
-
-    private final RestTemplate restTemplate;
-    private final KafkaSender kafkaSender;
-
-    @Value("${api.server.url}")
-    private String apiServerUrl;
-
-    @Value("${task.event.topic.name}")
-    private String taskEventPublishTopic;
-
-    public TaskExecutionService(RestTemplate restTemplate, KafkaSender kafkaSender) {
-        this.restTemplate = restTemplate;
-        this.kafkaSender = kafkaSender;
-    }
-
-    public void executeTaskAsync(long taskId) {
-
-        System.out.println("Executing task " + taskId + " as egress staging task");
-        TaskResource taskResource = this.restTemplate.getForObject("http://" + apiServerUrl + "/task/" + taskId, TaskResource.class);
-
-        publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.SCHEDULED);
-
-        this.executorService.execute(new Runnable() {
-            @Override
-            public void run() {
-                executeTask(taskResource);
-            }
-        });
-    }
-
-    public void executeTask(TaskResource taskResource) {
-        try {
-            Optional<TaskParamResource> sourceParam = taskResource.getTaskParams()
-                    .stream()
-                    .filter(taskParamResource -> "source".equals(taskParamResource.getKey()))
-                    .findFirst();
-
-            Optional<TaskParamResource> targetParam = taskResource.getTaskParams()
-                    .stream()
-                    .filter(taskParamResource -> "target".equals(taskParamResource.getKey()))
-                    .findFirst();
-
-            Optional<TaskParamResource> computeId = taskResource.getTaskParams()
-                    .stream()
-                    .filter(taskParamResource -> "compute-id".equals(taskParamResource.getKey()))
-                    .findFirst();
-
-            Optional<TaskParamResource> experimentDataDir = taskResource.getTaskParams()
-                    .stream()
-                    .filter(taskParamResource -> "exp-data-dir".equals(taskParamResource.getKey()))
-                    .findFirst();
-
-            String processDataDirectory = experimentDataDir
-                    .orElseThrow(() -> new Exception("exp-data-dir param can not be found in the params of task " +
-                            taskResource.getId())).getValue() + "/" + taskResource.getParentProcessId();
-
-
-            if (sourceParam.isPresent()) {
-                sourceParam.get().setValue(sourceParam.get().getValue().replace("{process-data-dir}", processDataDirectory));
-                if (targetParam.isPresent()) {
-                    publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.EXECUTING);
-
-                    ComputeResource computeResource = this.restTemplate.getForObject("http://" + this.apiServerUrl
-                            + "/compute/" + Long.parseLong(computeId.get().getValue()), ComputeResource.class);
-
-                    ComputeOperations operations;
-                    if ("SSH".equals(computeResource.getCommunicationType())) {
-                        operations = new SSHComputeOperations(computeResource.getHost(), computeResource.getUserName(), computeResource.getPassword());
-                    } else if ("Mock".equals(computeResource.getCommunicationType())) {
-                        operations = new MockComputeOperation(computeResource.getHost());
-                    } else {
-                        throw new Exception("No compatible communication method {" + computeResource.getCommunicationType() + "} not found for compute resource " + computeResource.getName());
-                    }
-
-                    try {
-
-                        String temporaryFile = "/tmp/" + UUID.randomUUID().toString();
-                        System.out.println("Downloading " + sourceParam.get().getValue() + " to " + temporaryFile +
-                                " from compute resource " + computeResource.getName());
-                        operations.transferDataOut(sourceParam.get().getValue(), temporaryFile, "SCP");
-
-                        RestTemplate template = new RestTemplate();
-                        LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
-                        map.add("file", new FileSystemResource(temporaryFile));
-                        HttpHeaders headers = new HttpHeaders();
-                        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
-
-                        HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<LinkedMultiValueMap<String, Object>>(
-                                map, headers);
-
-                        System.out.println("Uploading data file with task id " + taskResource.getId()
-                                + " and experiment output id " + targetParam.get().getValue() + " to data store");
-
-                        ResponseEntity<Long> result = template.exchange(
-                                "http://" + apiServerUrl + "/data/" + taskResource.getId()+ "/"
-                                        + targetParam.get().getValue() +"/upload", HttpMethod.POST, requestEntity, Long.class);
-
-                        publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(),
-                                TaskStatusResource.State.COMPLETED);
-
-                    } catch (Exception e) {
-
-                        e.printStackTrace();
-                        publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(),
-                                TaskStatusResource.State.FAILED, e.getMessage());
-                    }
-                } else {
-                    System.out.println("Target can not be null for task " + taskResource.getId());
-                    publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(),
-                            TaskStatusResource.State.FAILED, "Target can not be null for task " + taskResource.getId());
-                }
-            } else {
-                System.out.println("Source can not be null for task " + taskResource.getId());
-                publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(),
-                        TaskStatusResource.State.FAILED, "Source can not be null for task " + taskResource.getId());
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-            publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(),
-                    TaskStatusResource.State.FAILED, e.getMessage());
-        }
-    }
-
-    public void publishTaskStatus(long processId, long taskId, int status) {
-        publishTaskStatus(processId, taskId, status, "");
-    }
-
-    public void publishTaskStatus(long processId, long taskId, int status, String reason) {
-        this.kafkaSender.send(this.taskEventPublishTopic, processId + "-" + taskId,
-                processId + "," + taskId + "," + status + "," + reason);
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/resources/application.yml b/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/resources/application.yml
deleted file mode 100644
index 069dd61..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/resources/application.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-kafka:
-  bootstrap-servers: kafka.default.svc.cluster.local:9092
-  topic:
-    helloworld: helloworld.t
\ No newline at end of file
diff --git a/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/pom.xml b/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/pom.xml
deleted file mode 100644
index 43bca2c..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/pom.xml
+++ /dev/null
@@ -1,156 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-
-    Licensed to the Apache Software Foundation (ASF) under one
-    or more contributor license agreements.  See the NOTICE file
-    distributed with this work for additional information
-    regarding copyright ownership.  The ASF licenses this file
-    to you under the Apache License, Version 2.0 (the
-    "License"); you may not use this file except in compliance
-    with the License.  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing,
-    software distributed under the License is distributed on an
-    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-    KIND, either express or implied.  See the License for the
-    specific language governing permissions and limitations
-    under the License.
-
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <artifactId>airavata-kubernetes</artifactId>
-        <groupId>org.apache.airavata</groupId>
-        <version>1.0-SNAPSHOT</version>
-        <relativePath>../../../../pom.xml</relativePath>
-    </parent>
-    <modelVersion>4.0.0</modelVersion>
-
-    <artifactId>env-cleanup-task</artifactId>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.apache.airavata</groupId>
-            <artifactId>compute-resource-api</artifactId>
-            <version>1.0-SNAPSHOT</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.airavata</groupId>
-            <artifactId>api-resource</artifactId>
-            <version>1.0-SNAPSHOT</version>
-        </dependency>
-
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-web</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-freemarker</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.kafka</groupId>
-            <artifactId>spring-kafka</artifactId>
-        </dependency>
-    </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.5.1</version>
-                <configuration>
-                    <source>${java.version}</source>
-                    <target>${java.version}</target>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
-
-    <profiles>
-
-        <profile>
-            <id>jar</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <artifact-packaging>jar</artifact-packaging>
-            </properties>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.springframework.boot</groupId>
-                        <artifactId>spring-boot-maven-plugin</artifactId>
-                        <version>1.4.3.RELEASE</version>
-                        <executions>
-                            <execution>
-                                <goals>
-                                    <goal>repackage</goal>
-                                </goals>
-                            </execution>
-                        </executions>
-                    </plugin>
-                    <!-- Create a docker image that runs the executable jar-->
-                    <plugin>
-                        <groupId>com.spotify</groupId>
-                        <artifactId>docker-maven-plugin</artifactId>
-                        <version>1.0.0</version>
-                        <configuration>
-                            <imageName>${docker.image.prefix}/env-cleanup-task</imageName>
-                            <baseImage>java:openjdk-8-jdk-alpine</baseImage>
-                            <entryPoint>["java","-jar","/${project.build.finalName}.jar"]</entryPoint>
-                            <resources>
-                                <resource>
-                                    <targetPath>/</targetPath>
-                                    <directory>${project.build.directory}</directory>
-                                    <include>${project.build.finalName}.jar</include>
-                                </resource>
-                            </resources>
-                        </configuration>
-                        <executions>
-                            <execution>
-                                <goals>
-                                    <goal>build</goal>
-                                </goals>
-                            </execution>
-                        </executions>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-
-        <profile>
-            <id>war</id>
-            <properties>
-                <artifact-packaging>war</artifact-packaging>
-            </properties>
-            <dependencies>
-                <dependency>
-                    <groupId>org.springframework.boot</groupId>
-                    <artifactId>spring-boot-starter-tomcat</artifactId>
-                    <scope>provided</scope>
-                </dependency>
-            </dependencies>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.springframework.boot</groupId>
-                        <artifactId>spring-boot-maven-plugin</artifactId>
-                        <version>1.4.3.RELEASE</version>
-                        <configuration>
-                            <!-- this will get rid of version info from war file name -->
-                            <finalName>env-cleanup</finalName>
-                        </configuration>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-    </profiles>
-</project>
\ No newline at end of file
diff --git a/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/java/org/apache/airavata/k8s/task/cleanup/Application.java b/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/java/org/apache/airavata/k8s/task/cleanup/Application.java
deleted file mode 100644
index 1e5cf1f..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/java/org/apache/airavata/k8s/task/cleanup/Application.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- *
- * 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.airavata.k8s.task.cleanup;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.web.client.RestTemplateBuilder;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.ComponentScan;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.client.RestTemplate;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-@SpringBootApplication
-@Configuration
-@ComponentScan
-public class Application {
-
-    public static void main(String[] args) {
-        SpringApplication.run(Application.class, args);
-    }
-
-    @Bean
-    public RestTemplate restTemplate(RestTemplateBuilder builder) {
-        return builder.build();
-    }
-}
\ No newline at end of file
diff --git a/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/java/org/apache/airavata/k8s/task/cleanup/messaging/KafkaReceiver.java b/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/java/org/apache/airavata/k8s/task/cleanup/messaging/KafkaReceiver.java
deleted file mode 100644
index 01aaa51..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/java/org/apache/airavata/k8s/task/cleanup/messaging/KafkaReceiver.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- *
- * 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.airavata.k8s.task.cleanup.messaging;
-
-import org.apache.airavata.k8s.task.cleanup.service.TaskExecutionService;
-import org.springframework.kafka.annotation.KafkaListener;
-import org.springframework.kafka.support.Acknowledgment;
-
-import javax.annotation.Resource;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-public class KafkaReceiver {
-
-    @Resource
-    private TaskExecutionService taskExecutionService;
-
-    @KafkaListener(topics = "${task.read.topic.name}")
-    public void receiveTasks(String payload, Acknowledgment ack) {
-        System.out.println("received task=" + payload);
-        taskExecutionService.executeTaskAsync(Long.parseLong(payload));
-        ack.acknowledge();
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/java/org/apache/airavata/k8s/task/cleanup/messaging/KafkaSender.java b/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/java/org/apache/airavata/k8s/task/cleanup/messaging/KafkaSender.java
deleted file mode 100644
index 8293d31..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/java/org/apache/airavata/k8s/task/cleanup/messaging/KafkaSender.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- *
- * 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.airavata.k8s.task.cleanup.messaging;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.kafka.core.KafkaTemplate;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-public class KafkaSender {
-
-    @Autowired
-    private KafkaTemplate<String, String> kafkaTemplate;
-
-    public void send(String topic, String payload) {
-        kafkaTemplate.send(topic, payload);
-    }
-
-    public void send(String topic, String key, String payload) {
-        kafkaTemplate.send(topic, key, payload);
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/java/org/apache/airavata/k8s/task/cleanup/messaging/ReceiverConfig.java b/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/java/org/apache/airavata/k8s/task/cleanup/messaging/ReceiverConfig.java
deleted file mode 100644
index 941ba11..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/java/org/apache/airavata/k8s/task/cleanup/messaging/ReceiverConfig.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- *
- * 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.airavata.k8s.task.cleanup.messaging;
-
-import org.apache.kafka.clients.consumer.ConsumerConfig;
-import org.apache.kafka.common.serialization.StringDeserializer;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.kafka.annotation.EnableKafka;
-import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
-import org.springframework.kafka.config.KafkaListenerContainerFactory;
-import org.springframework.kafka.core.ConsumerFactory;
-import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
-import org.springframework.kafka.listener.AbstractMessageListenerContainer;
-import org.springframework.kafka.listener.ConcurrentMessageListenerContainer;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-@Configuration
-@EnableKafka
-public class ReceiverConfig {
-
-    @Value("${kafka.bootstrap-servers}")
-    private String bootstrapServers;
-
-    @Value("${task.group.name}")
-    private String taskGroupName;
-
-    @Bean
-    public Map<String, Object> consumerConfigs() {
-        Map<String, Object> props = new HashMap<>();
-        // list of host:port pairs used for establishing the initial connections to the Kakfa cluster
-        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
-        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
-        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
-        // allows a pool of processes to divide the work of consuming and processing records
-        props.put(ConsumerConfig.GROUP_ID_CONFIG, taskGroupName);
-        props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
-        return props;
-    }
-
-    @Bean
-    public ConsumerFactory<String, String> consumerFactory() {
-        return new DefaultKafkaConsumerFactory<String, String>(consumerConfigs());
-    }
-
-    @Bean
-    public KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, String>> kafkaListenerContainerFactory() {
-        ConcurrentKafkaListenerContainerFactory<String, String> factory =
-                new ConcurrentKafkaListenerContainerFactory<>();
-        factory.setConsumerFactory(consumerFactory());
-        factory.getContainerProperties().setAckMode(AbstractMessageListenerContainer.AckMode.MANUAL_IMMEDIATE);
-        return factory;
-    }
-
-    @Bean
-    public KafkaReceiver receiver() {
-        return new KafkaReceiver();
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/java/org/apache/airavata/k8s/task/cleanup/messaging/SenderConfig.java b/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/java/org/apache/airavata/k8s/task/cleanup/messaging/SenderConfig.java
deleted file mode 100644
index a84cecf..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/java/org/apache/airavata/k8s/task/cleanup/messaging/SenderConfig.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- *
- * 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.airavata.k8s.task.cleanup.messaging;
-
-import org.apache.kafka.clients.producer.ProducerConfig;
-import org.apache.kafka.common.serialization.StringSerializer;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.kafka.core.DefaultKafkaProducerFactory;
-import org.springframework.kafka.core.KafkaTemplate;
-import org.springframework.kafka.core.ProducerFactory;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-@Configuration
-public class SenderConfig {
-    @Value("${kafka.bootstrap-servers}")
-    private String bootstrapServers;
-
-    @Bean
-    public Map<String, Object> producerConfigs() {
-        Map<String, Object> props = new HashMap<>();
-        // list of host:port pairs used for establishing the initial connections to the Kakfa cluster
-        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
-        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
-        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
-        return props;
-    }
-
-    @Bean
-    public ProducerFactory<String, String> producerFactory() {
-        return new DefaultKafkaProducerFactory<String, String>(producerConfigs());
-    }
-
-    @Bean
-    public KafkaTemplate<String, String> kafkaTemplate() {
-        return new KafkaTemplate<>(producerFactory());
-    }
-
-    @Bean
-    public KafkaSender kafkaSender() {
-        return new KafkaSender();
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/java/org/apache/airavata/k8s/task/cleanup/service/TaskExecutionService.java b/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/java/org/apache/airavata/k8s/task/cleanup/service/TaskExecutionService.java
deleted file mode 100644
index 4c9cc66..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/java/org/apache/airavata/k8s/task/cleanup/service/TaskExecutionService.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/**
- *
- * 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.airavata.k8s.task.cleanup.service;
-
-import org.apache.airavata.k8s.api.resources.compute.ComputeResource;
-import org.apache.airavata.k8s.api.resources.task.TaskParamResource;
-import org.apache.airavata.k8s.api.resources.task.TaskResource;
-import org.apache.airavata.k8s.api.resources.task.TaskStatusResource;
-import org.apache.airavata.k8s.compute.api.ComputeOperations;
-import org.apache.airavata.k8s.compute.api.ExecutionResult;
-import org.apache.airavata.k8s.compute.impl.MockComputeOperation;
-import org.apache.airavata.k8s.compute.impl.SSHComputeOperations;
-import org.apache.airavata.k8s.task.cleanup.messaging.KafkaSender;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-import org.springframework.web.client.RestTemplate;
-
-import java.util.Optional;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-@Service
-public class TaskExecutionService {
-
-    private final ExecutorService executorService = Executors.newFixedThreadPool(10);
-
-    private final RestTemplate restTemplate;
-    private final KafkaSender kafkaSender;
-
-    @Value("${api.server.url}")
-    private String apiServerUrl;
-
-    @Value("${task.event.topic.name}")
-    private String taskEventPublishTopic;
-
-    public TaskExecutionService(RestTemplate restTemplate, KafkaSender kafkaSender) {
-        this.restTemplate = restTemplate;
-        this.kafkaSender = kafkaSender;
-    }
-
-    public void executeTaskAsync(long taskId) {
-
-        System.out.println("Executing task " + taskId + " as env cleanup task");
-        TaskResource taskResource = this.restTemplate.getForObject("http://" + apiServerUrl + "/task/" + taskId, TaskResource.class);
-
-        publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.SCHEDULED);
-
-        this.executorService.execute(new Runnable() {
-            @Override
-            public void run() {
-                executeTask(taskResource);
-            }
-        });
-    }
-
-    public void executeTask(TaskResource taskResource) {
-
-        try {
-            Optional<TaskParamResource> commandParam = taskResource.getTaskParams()
-                    .stream()
-                    .filter(taskParamResource -> "command".equals(taskParamResource.getKey()))
-                    .findFirst();
-
-            Optional<TaskParamResource> computeId = taskResource.getTaskParams()
-                    .stream()
-                    .filter(taskParamResource -> "compute-id".equals(taskParamResource.getKey()))
-                    .findFirst();
-
-            Optional<TaskParamResource> experimentDataDir = taskResource.getTaskParams()
-                    .stream()
-                    .filter(taskParamResource -> "exp-data-dir".equals(taskParamResource.getKey()))
-                    .findFirst();
-
-            String processDataDirectory = experimentDataDir
-                    .orElseThrow(() -> new Exception("exp-data-dir param can not be found in the params of task " +
-                            taskResource.getId())).getValue() + "/" + taskResource.getParentProcessId();
-
-            commandParam.ifPresent(taskParamResource -> {
-
-                try {
-                    String command = taskParamResource.getValue();
-                    command = command.replace("{process-data-dir}", processDataDirectory);
-
-                    System.out.println("Executing command " + command);
-
-                    publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.EXECUTING);
-
-                    // TODO fetch this from the catalog
-                    ComputeResource computeResource = this.restTemplate.getForObject("http://" + this.apiServerUrl
-                            + "/compute/" + Long.parseLong(computeId.get().getValue()), ComputeResource.class);
-
-                    // TODO fetch this from the catalog
-                    ComputeOperations operations;
-                    if ("SSH".equals(computeResource.getCommunicationType())) {
-                        operations = new SSHComputeOperations(computeResource.getHost(), computeResource.getUserName(), computeResource.getPassword());
-                    } else if ("Mock".equals(computeResource.getCommunicationType())) {
-                        operations = new MockComputeOperation(computeResource.getHost());
-                    } else {
-                        throw new Exception("No compatible communication method {" + computeResource.getCommunicationType() + "} not found for compute resource " + computeResource.getName());
-                    }
-
-                    ExecutionResult executionResult = operations.executeCommand(command);
-                    if (executionResult.getExitStatus() == 0) {
-                        publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.COMPLETED);
-                    } else if (executionResult.getExitStatus() == -1) {
-                        publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED, "Process didn't exit successfully");
-                    } else {
-                        publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED, "Process exited with error status " + executionResult.getExitStatus());
-                    }
-
-                } catch (Exception e) {
-                    e.printStackTrace();
-                    publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED, e.getMessage());
-                }
-            });
-        } catch (Exception e) {
-            e.printStackTrace();
-            publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED, e.getMessage());
-        }
-    }
-
-    public void publishTaskStatus(long processId, long taskId, int status) {
-        publishTaskStatus(processId, taskId, status, "");
-    }
-
-    public void publishTaskStatus(long processId, long taskId, int status, String reason) {
-        this.kafkaSender.send(this.taskEventPublishTopic, processId + "-" + taskId,
-                processId + "," + taskId + "," + status + "," + reason);
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/resources/application.properties b/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/resources/application.properties
deleted file mode 100644
index f9d49a7..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/resources/application.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-server.port = 8591
-api.server.url = api-server.default.svc.cluster.local:8080
-task.group.name = env-cleanup
-task.event.topic.name = airavata-task-event
-task.read.topic.name = airavata-task-env-cleanup
\ No newline at end of file
diff --git a/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/resources/application.yml b/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/resources/application.yml
deleted file mode 100644
index 069dd61..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/env-cleanup-task/src/main/resources/application.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-kafka:
-  bootstrap-servers: kafka.default.svc.cluster.local:9092
-  topic:
-    helloworld: helloworld.t
\ No newline at end of file
diff --git a/airavata-kubernetes/modules/microservices/tasks/env-setup-task/pom.xml b/airavata-kubernetes/modules/microservices/tasks/env-setup-task/pom.xml
deleted file mode 100644
index d011d7b..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/env-setup-task/pom.xml
+++ /dev/null
@@ -1,157 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-
-    Licensed to the Apache Software Foundation (ASF) under one
-    or more contributor license agreements.  See the NOTICE file
-    distributed with this work for additional information
-    regarding copyright ownership.  The ASF licenses this file
-    to you under the Apache License, Version 2.0 (the
-    "License"); you may not use this file except in compliance
-    with the License.  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing,
-    software distributed under the License is distributed on an
-    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-    KIND, either express or implied.  See the License for the
-    specific language governing permissions and limitations
-    under the License.
-
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <artifactId>airavata-kubernetes</artifactId>
-        <groupId>org.apache.airavata</groupId>
-        <version>1.0-SNAPSHOT</version>
-        <relativePath>../../../../pom.xml</relativePath>
-    </parent>
-    <modelVersion>4.0.0</modelVersion>
-
-    <artifactId>env-setup-task</artifactId>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.apache.airavata</groupId>
-            <artifactId>compute-resource-api</artifactId>
-            <version>1.0-SNAPSHOT</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.airavata</groupId>
-            <artifactId>api-resource</artifactId>
-            <version>1.0-SNAPSHOT</version>
-        </dependency>
-
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-web</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-freemarker</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.kafka</groupId>
-            <artifactId>spring-kafka</artifactId>
-        </dependency>
-    </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.5.1</version>
-                <configuration>
-                    <source>${java.version}</source>
-                    <target>${java.version}</target>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
-
-    <profiles>
-
-        <profile>
-            <id>jar</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <artifact-packaging>jar</artifact-packaging>
-            </properties>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.springframework.boot</groupId>
-                        <artifactId>spring-boot-maven-plugin</artifactId>
-                        <version>1.4.3.RELEASE</version>
-                        <executions>
-                            <execution>
-                                <goals>
-                                    <goal>repackage</goal>
-                                </goals>
-                            </execution>
-                        </executions>
-                    </plugin>
-                    <!-- Create a docker image that runs the executable jar-->
-                    <plugin>
-                        <groupId>com.spotify</groupId>
-                        <artifactId>docker-maven-plugin</artifactId>
-                        <version>1.0.0</version>
-                        <configuration>
-                            <imageName>${docker.image.prefix}/env-setup-task</imageName>
-                            <baseImage>java:openjdk-8-jdk-alpine</baseImage>
-                            <entryPoint>["java","-jar","/${project.build.finalName}.jar"]</entryPoint>
-                            <resources>
-                                <resource>
-                                    <targetPath>/</targetPath>
-                                    <directory>${project.build.directory}</directory>
-                                    <include>${project.build.finalName}.jar</include>
-                                </resource>
-                            </resources>
-                        </configuration>
-                        <executions>
-                            <execution>
-                                <goals>
-                                    <goal>build</goal>
-                                </goals>
-                            </execution>
-                        </executions>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-
-        <profile>
-            <id>war</id>
-            <properties>
-                <artifact-packaging>war</artifact-packaging>
-            </properties>
-            <dependencies>
-                <dependency>
-                    <groupId>org.springframework.boot</groupId>
-                    <artifactId>spring-boot-starter-tomcat</artifactId>
-                    <scope>provided</scope>
-                </dependency>
-            </dependencies>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.springframework.boot</groupId>
-                        <artifactId>spring-boot-maven-plugin</artifactId>
-                        <version>1.4.3.RELEASE</version>
-                        <configuration>
-                            <!-- this will get rid of version info from war file name -->
-                            <finalName>env-setup</finalName>
-                        </configuration>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-    </profiles>
-
-</project>
\ No newline at end of file
diff --git a/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/java/org/apache/airavata/k8s/task/env/setup/Application.java b/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/java/org/apache/airavata/k8s/task/env/setup/Application.java
deleted file mode 100644
index 43881fc..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/java/org/apache/airavata/k8s/task/env/setup/Application.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- *
- * 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.airavata.k8s.task.env.setup;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.web.client.RestTemplateBuilder;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.ComponentScan;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.client.RestTemplate;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-@SpringBootApplication
-@Configuration
-@ComponentScan
-public class Application {
-
-    public static void main(String[] args) {
-        SpringApplication.run(Application.class, args);
-    }
-
-    @Bean
-    public RestTemplate restTemplate(RestTemplateBuilder builder) {
-        return builder.build();
-    }
-}
\ No newline at end of file
diff --git a/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/java/org/apache/airavata/k8s/task/env/setup/messaging/KafkaReceiver.java b/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/java/org/apache/airavata/k8s/task/env/setup/messaging/KafkaReceiver.java
deleted file mode 100644
index 38c7bf4..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/java/org/apache/airavata/k8s/task/env/setup/messaging/KafkaReceiver.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- *
- * 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.airavata.k8s.task.env.setup.messaging;
-
-import org.apache.airavata.k8s.task.env.setup.service.TaskExecutionService;
-import org.springframework.kafka.annotation.KafkaListener;
-import org.springframework.kafka.support.Acknowledgment;
-
-import javax.annotation.Resource;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-public class KafkaReceiver {
-
-    @Resource
-    private TaskExecutionService taskExecutionService;
-
-    @KafkaListener(topics = "${task.read.topic.name}")
-    public void receiveTasks(String payload, Acknowledgment ack) {
-        System.out.println("received task=" + payload);
-        taskExecutionService.executeTaskAsync(Long.parseLong(payload));
-        ack.acknowledge();
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/java/org/apache/airavata/k8s/task/env/setup/messaging/KafkaSender.java b/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/java/org/apache/airavata/k8s/task/env/setup/messaging/KafkaSender.java
deleted file mode 100644
index 5cf6499..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/java/org/apache/airavata/k8s/task/env/setup/messaging/KafkaSender.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- *
- * 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.airavata.k8s.task.env.setup.messaging;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.kafka.core.KafkaTemplate;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-public class KafkaSender {
-
-    @Autowired
-    private KafkaTemplate<String, String> kafkaTemplate;
-
-    public void send(String topic, String payload) {
-        kafkaTemplate.send(topic, payload);
-    }
-
-    public void send(String topic, String key, String payload) {
-        kafkaTemplate.send(topic, key, payload);
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/java/org/apache/airavata/k8s/task/env/setup/messaging/ReceiverConfig.java b/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/java/org/apache/airavata/k8s/task/env/setup/messaging/ReceiverConfig.java
deleted file mode 100644
index c26395e..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/java/org/apache/airavata/k8s/task/env/setup/messaging/ReceiverConfig.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- *
- * 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.airavata.k8s.task.env.setup.messaging;
-
-import org.apache.kafka.clients.consumer.ConsumerConfig;
-import org.apache.kafka.common.serialization.StringDeserializer;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.kafka.annotation.EnableKafka;
-import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
-import org.springframework.kafka.config.KafkaListenerContainerFactory;
-import org.springframework.kafka.core.ConsumerFactory;
-import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
-import org.springframework.kafka.listener.AbstractMessageListenerContainer;
-import org.springframework.kafka.listener.ConcurrentMessageListenerContainer;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-@Configuration
-@EnableKafka
-public class ReceiverConfig {
-
-    @Value("${kafka.bootstrap-servers}")
-    private String bootstrapServers;
-
-    @Value("${task.group.name}")
-    private String taskGroupName;
-
-    @Bean
-    public Map<String, Object> consumerConfigs() {
-        Map<String, Object> props = new HashMap<>();
-        // list of host:port pairs used for establishing the initial connections to the Kakfa cluster
-        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
-        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
-        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
-        // allows a pool of processes to divide the work of consuming and processing records
-        props.put(ConsumerConfig.GROUP_ID_CONFIG, taskGroupName);
-        props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
-        return props;
-    }
-
-    @Bean
-    public ConsumerFactory<String, String> consumerFactory() {
-        return new DefaultKafkaConsumerFactory<String, String>(consumerConfigs());
-    }
-
-    @Bean
-    public KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, String>> kafkaListenerContainerFactory() {
-        ConcurrentKafkaListenerContainerFactory<String, String> factory =
-                new ConcurrentKafkaListenerContainerFactory<>();
-        factory.setConsumerFactory(consumerFactory());
-        factory.getContainerProperties().setAckMode(AbstractMessageListenerContainer.AckMode.MANUAL_IMMEDIATE);
-        return factory;
-    }
-
-    @Bean
-    public KafkaReceiver receiver() {
-        return new KafkaReceiver();
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/java/org/apache/airavata/k8s/task/env/setup/messaging/SenderConfig.java b/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/java/org/apache/airavata/k8s/task/env/setup/messaging/SenderConfig.java
deleted file mode 100644
index ce8b975..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/java/org/apache/airavata/k8s/task/env/setup/messaging/SenderConfig.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- *
- * 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.airavata.k8s.task.env.setup.messaging;
-
-import org.apache.kafka.clients.producer.ProducerConfig;
-import org.apache.kafka.common.serialization.StringSerializer;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.kafka.core.DefaultKafkaProducerFactory;
-import org.springframework.kafka.core.KafkaTemplate;
-import org.springframework.kafka.core.ProducerFactory;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-@Configuration
-public class SenderConfig {
-    @Value("${kafka.bootstrap-servers}")
-    private String bootstrapServers;
-
-    @Bean
-    public Map<String, Object> producerConfigs() {
-        Map<String, Object> props = new HashMap<>();
-        // list of host:port pairs used for establishing the initial connections to the Kakfa cluster
-        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
-        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
-        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
-        return props;
-    }
-
-    @Bean
-    public ProducerFactory<String, String> producerFactory() {
-        return new DefaultKafkaProducerFactory<String, String>(producerConfigs());
-    }
-
-    @Bean
-    public KafkaTemplate<String, String> kafkaTemplate() {
-        return new KafkaTemplate<>(producerFactory());
-    }
-
-    @Bean
-    public KafkaSender kafkaSender() {
-        return new KafkaSender();
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/java/org/apache/airavata/k8s/task/env/setup/service/TaskExecutionService.java b/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/java/org/apache/airavata/k8s/task/env/setup/service/TaskExecutionService.java
deleted file mode 100644
index 7e2c2da..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/java/org/apache/airavata/k8s/task/env/setup/service/TaskExecutionService.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/**
- *
- * 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.airavata.k8s.task.env.setup.service;
-
-import org.apache.airavata.k8s.api.resources.compute.ComputeResource;
-import org.apache.airavata.k8s.api.resources.task.TaskParamResource;
-import org.apache.airavata.k8s.api.resources.task.TaskResource;
-import org.apache.airavata.k8s.api.resources.task.TaskStatusResource;
-import org.apache.airavata.k8s.compute.api.ComputeOperations;
-import org.apache.airavata.k8s.compute.api.ExecutionResult;
-import org.apache.airavata.k8s.compute.impl.MockComputeOperation;
-import org.apache.airavata.k8s.compute.impl.SSHComputeOperations;
-import org.apache.airavata.k8s.task.env.setup.messaging.KafkaSender;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-import org.springframework.web.client.RestTemplate;
-
-import java.util.Optional;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-@Service
-public class TaskExecutionService {
-
-    private final ExecutorService executorService = Executors.newFixedThreadPool(10);
-
-    private final RestTemplate restTemplate;
-    private final KafkaSender kafkaSender;
-
-    @Value("${api.server.url}")
-    private String apiServerUrl;
-
-    @Value("${task.event.topic.name}")
-    private String taskEventPublishTopic;
-
-    public TaskExecutionService(RestTemplate restTemplate, KafkaSender kafkaSender) {
-        this.restTemplate = restTemplate;
-        this.kafkaSender = kafkaSender;
-    }
-
-    public void executeTaskAsync(long taskId) {
-
-        System.out.println("Executing task " + taskId + " as env setup task");
-        TaskResource taskResource = this.restTemplate.getForObject("http://" + apiServerUrl + "/task/" + taskId, TaskResource.class);
-
-        publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.SCHEDULED);
-
-        this.executorService.execute(new Runnable() {
-            @Override
-            public void run() {
-                executeTask(taskResource);
-            }
-        });
-    }
-
-    public void executeTask(TaskResource taskResource) {
-
-        try {
-
-            Optional<TaskParamResource> commandParam = taskResource.getTaskParams()
-                    .stream()
-                    .filter(taskParamResource -> "command".equals(taskParamResource.getKey()))
-                    .findFirst();
-
-            Optional<TaskParamResource> computeId = taskResource.getTaskParams()
-                    .stream()
-                    .filter(taskParamResource -> "compute-id".equals(taskParamResource.getKey()))
-                    .findFirst();
-
-            Optional<TaskParamResource> experimentDataDir = taskResource.getTaskParams()
-                    .stream()
-                    .filter(taskParamResource -> "exp-data-dir".equals(taskParamResource.getKey()))
-                    .findFirst();
-
-            String processDataDirectory = experimentDataDir
-                    .orElseThrow(() -> new Exception("exp-data-dir param can not be found in the params of task " +
-                            taskResource.getId())).getValue() + "/" + taskResource.getParentProcessId();
-
-            commandParam.ifPresent(taskParamResource -> {
-                try {
-
-                    String command = taskParamResource.getValue();
-                    command = command.replace("{process-data-dir}", processDataDirectory);
-                    System.out.println("Executing command " + command);
-
-                    publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.EXECUTING);
-
-                    ComputeResource computeResource = this.restTemplate.getForObject("http://" + this.apiServerUrl
-                            + "/compute/" + Long.parseLong(computeId.get().getValue()), ComputeResource.class);
-
-                    // TODO fetch this from the catalog
-                    ComputeOperations operations;
-                    if ("SSH".equals(computeResource.getCommunicationType())) {
-                        operations = new SSHComputeOperations(computeResource.getHost(), computeResource.getUserName(), computeResource.getPassword());
-                    } else if ("Mock".equals(computeResource.getCommunicationType())) {
-                        operations = new MockComputeOperation(computeResource.getHost());
-                    } else {
-                        throw new Exception("No compatible communication method {" + computeResource.getCommunicationType() + "} not found for compute resource " + computeResource.getName());
-                    }
-
-                    ExecutionResult executionResult = operations.executeCommand(command);
-                    if (executionResult.getExitStatus() == 0) {
-                        publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.COMPLETED);
-                    } else if (executionResult.getExitStatus() == -1) {
-                        publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED, "Process didn't exit successfully");
-                    } else {
-                        publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED, "Process exited with error status " + executionResult.getExitStatus());
-                    }
-
-                } catch (Exception e) {
-                    e.printStackTrace();
-                    publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED, e.getMessage());
-
-                }
-            });
-        } catch (Exception e) {
-            e.printStackTrace();
-            publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED, e.getMessage());
-        }
-    }
-
-    public void publishTaskStatus(long processId, long taskId, int status) {
-        publishTaskStatus(processId, taskId, status, "");
-    }
-
-    public void publishTaskStatus(long processId, long taskId, int status, String reason) {
-        this.kafkaSender.send(this.taskEventPublishTopic, processId + "-" + taskId,
-                processId + "," + taskId + "," + status + "," + reason);
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/resources/application.properties b/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/resources/application.properties
deleted file mode 100644
index 0ac80de..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/env-setup-task/src/main/resources/application.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-server.port = 8392
-api.server.url = api-server.default.svc.cluster.local:8080
-task.group.name = env-setup
-task.event.topic.name = airavata-task-event
-task.read.topic.name = airavata-task-env-setup
\ No newline at end of file
diff --git a/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/java/org/apache/airavata/k8s/task/ingress/messaging/KafkaReceiver.java b/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/java/org/apache/airavata/k8s/task/ingress/messaging/KafkaReceiver.java
deleted file mode 100644
index 912add3..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/java/org/apache/airavata/k8s/task/ingress/messaging/KafkaReceiver.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- *
- * 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.airavata.k8s.task.ingress.messaging;
-
-import org.apache.airavata.k8s.task.ingress.service.TaskExecutionService;
-import org.springframework.kafka.annotation.KafkaListener;
-import org.springframework.kafka.support.Acknowledgment;
-
-import javax.annotation.Resource;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-public class KafkaReceiver {
-
-    @Resource
-    private TaskExecutionService taskExecutionService;
-
-    @KafkaListener(topics = "${task.read.topic.name}")
-    public void receiveTasks(String payload, Acknowledgment ack) {
-        System.out.println("received task=" + payload);
-        taskExecutionService.executeTaskAsync(Long.parseLong(payload));
-        ack.acknowledge();
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/java/org/apache/airavata/k8s/task/ingress/messaging/KafkaSender.java b/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/java/org/apache/airavata/k8s/task/ingress/messaging/KafkaSender.java
deleted file mode 100644
index bf0b7c1..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/java/org/apache/airavata/k8s/task/ingress/messaging/KafkaSender.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- *
- * 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.airavata.k8s.task.ingress.messaging;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.kafka.core.KafkaTemplate;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-public class KafkaSender {
-
-    @Autowired
-    private KafkaTemplate<String, String> kafkaTemplate;
-
-    public void send(String topic, String payload) {
-        kafkaTemplate.send(topic, payload);
-    }
-
-    public void send(String topic, String key, String payload) {
-        kafkaTemplate.send(topic, key, payload);
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/java/org/apache/airavata/k8s/task/ingress/messaging/ReceiverConfig.java b/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/java/org/apache/airavata/k8s/task/ingress/messaging/ReceiverConfig.java
deleted file mode 100644
index cfbb3f9..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/java/org/apache/airavata/k8s/task/ingress/messaging/ReceiverConfig.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- *
- * 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.airavata.k8s.task.ingress.messaging;
-
-import org.apache.kafka.clients.consumer.ConsumerConfig;
-import org.apache.kafka.common.serialization.StringDeserializer;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.kafka.annotation.EnableKafka;
-import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
-import org.springframework.kafka.config.KafkaListenerContainerFactory;
-import org.springframework.kafka.core.ConsumerFactory;
-import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
-import org.springframework.kafka.listener.AbstractMessageListenerContainer;
-import org.springframework.kafka.listener.ConcurrentMessageListenerContainer;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-@Configuration
-@EnableKafka
-public class ReceiverConfig {
-
-    @Value("${kafka.bootstrap-servers}")
-    private String bootstrapServers;
-
-    @Value("${task.group.name}")
-    private String taskGroupName;
-
-    @Bean
-    public Map<String, Object> consumerConfigs() {
-        Map<String, Object> props = new HashMap<>();
-        // list of host:port pairs used for establishing the initial connections to the Kakfa cluster
-        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
-        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
-        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
-        // allows a pool of processes to divide the work of consuming and processing records
-        props.put(ConsumerConfig.GROUP_ID_CONFIG, taskGroupName);
-        props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
-        return props;
-    }
-
-    @Bean
-    public ConsumerFactory<String, String> consumerFactory() {
-        return new DefaultKafkaConsumerFactory<String, String>(consumerConfigs());
-    }
-
-    @Bean
-    public KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, String>> kafkaListenerContainerFactory() {
-        ConcurrentKafkaListenerContainerFactory<String, String> factory =
-                new ConcurrentKafkaListenerContainerFactory<>();
-        factory.setConsumerFactory(consumerFactory());
-        factory.getContainerProperties().setAckMode(AbstractMessageListenerContainer.AckMode.MANUAL_IMMEDIATE);
-        return factory;
-    }
-
-    @Bean
-    public KafkaReceiver receiver() {
-        return new KafkaReceiver();
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/java/org/apache/airavata/k8s/task/ingress/messaging/SenderConfig.java b/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/java/org/apache/airavata/k8s/task/ingress/messaging/SenderConfig.java
deleted file mode 100644
index 8a9efbb..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/java/org/apache/airavata/k8s/task/ingress/messaging/SenderConfig.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- *
- * 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.airavata.k8s.task.ingress.messaging;
-
-import org.apache.kafka.clients.producer.ProducerConfig;
-import org.apache.kafka.common.serialization.StringSerializer;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.kafka.core.DefaultKafkaProducerFactory;
-import org.springframework.kafka.core.KafkaTemplate;
-import org.springframework.kafka.core.ProducerFactory;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-@Configuration
-public class SenderConfig {
-    @Value("${kafka.bootstrap-servers}")
-    private String bootstrapServers;
-
-    @Bean
-    public Map<String, Object> producerConfigs() {
-        Map<String, Object> props = new HashMap<>();
-        // list of host:port pairs used for establishing the initial connections to the Kakfa cluster
-        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
-        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
-        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
-        return props;
-    }
-
-    @Bean
-    public ProducerFactory<String, String> producerFactory() {
-        return new DefaultKafkaProducerFactory<String, String>(producerConfigs());
-    }
-
-    @Bean
-    public KafkaTemplate<String, String> kafkaTemplate() {
-        return new KafkaTemplate<>(producerFactory());
-    }
-
-    @Bean
-    public KafkaSender kafkaSender() {
-        return new KafkaSender();
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/java/org/apache/airavata/k8s/task/ingress/service/TaskExecutionService.java b/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/java/org/apache/airavata/k8s/task/ingress/service/TaskExecutionService.java
deleted file mode 100644
index 2fafa81..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/ingress-staging-task/src/main/java/org/apache/airavata/k8s/task/ingress/service/TaskExecutionService.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/**
- *
- * 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.airavata.k8s.task.ingress.service;
-
-import org.apache.airavata.k8s.api.resources.task.TaskParamResource;
-import org.apache.airavata.k8s.api.resources.task.TaskResource;
-import org.apache.airavata.k8s.api.resources.task.TaskStatusResource;
-import org.apache.airavata.k8s.compute.api.ComputeOperations;
-import org.apache.airavata.k8s.compute.impl.MockComputeOperation;
-import org.apache.airavata.k8s.task.ingress.messaging.KafkaSender;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.core.io.ClassPathResource;
-import org.springframework.core.io.FileSystemResource;
-import org.springframework.http.*;
-import org.springframework.stereotype.Service;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.web.client.RestTemplate;
-
-import java.io.File;
-import java.util.Optional;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-@Service
-public class TaskExecutionService {
-
-    private final ExecutorService executorService = Executors.newFixedThreadPool(10);
-
-    private final RestTemplate restTemplate;
-    private final KafkaSender kafkaSender;
-
-    @Value("${api.server.url}")
-    private String apiServerUrl;
-
-    @Value("${task.event.topic.name}")
-    private String taskEventPublishTopic;
-
-    public TaskExecutionService(RestTemplate restTemplate, KafkaSender kafkaSender) {
-        this.restTemplate = restTemplate;
-        this.kafkaSender = kafkaSender;
-    }
-
-    public void executeTaskAsync(long taskId) {
-
-        System.out.println("Executing task " + taskId + " as ingress task");
-        TaskResource taskResource = this.restTemplate.getForObject("http://" + apiServerUrl + "/task/" + taskId, TaskResource.class);
-
-        publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.SCHEDULED);
-
-        this.executorService.execute(new Runnable() {
-            @Override
-            public void run() {
-                executeTask(taskResource);
-            }
-        });
-    }
-
-    public void executeTask(TaskResource taskResource) {
-
-        Optional<TaskParamResource> sourceParam = taskResource.getTaskParams()
-                .stream()
-                .filter(taskParamResource -> "source".equals(taskParamResource.getKey()))
-                .findFirst();
-
-        Optional<TaskParamResource> targetParam = taskResource.getTaskParams()
-                .stream()
-                .filter(taskParamResource -> "target".equals(taskParamResource.getKey()))
-                .findFirst();
-
-        Optional<TaskParamResource> computeName = taskResource.getTaskParams()
-                .stream()
-                .filter(taskParamResource -> "compute-name".equals(taskParamResource.getKey()))
-                .findFirst();
-
-        if (sourceParam.isPresent()) {
-            if (targetParam.isPresent()) {
-                publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.EXECUTING);
-                ComputeOperations computeOperations = new MockComputeOperation(computeName.get().getValue());
-
-                try {
-                    computeOperations.transferDataIn(sourceParam.get().getValue(), targetParam.get().getValue(), "SCP");
-                    publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.COMPLETED);
-
-                } catch (Exception e) {
-
-                    e.printStackTrace();
-                    publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED);
-                }
-            } else {
-                System.out.println("Source can not be null for task " + taskResource.getId());
-                publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED);
-            }
-        } else {
-            System.out.println("Source can not be null for task " + taskResource.getId());
-            publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED);
-        }
-    }
-
-    public void publishTaskStatus(long processId, long taskId, int status) {
-        this.kafkaSender.send(this.taskEventPublishTopic, processId + "-" + taskId,
-                processId + "," + taskId + "," + status);
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/java/org/apache/airavata/k8s/task/job/messaging/ReceiverConfig.java b/airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/java/org/apache/airavata/k8s/task/job/messaging/ReceiverConfig.java
deleted file mode 100644
index 9a69ecf..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/java/org/apache/airavata/k8s/task/job/messaging/ReceiverConfig.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- *
- * 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.airavata.k8s.task.job.messaging;
-
-import org.apache.kafka.clients.consumer.ConsumerConfig;
-import org.apache.kafka.common.serialization.StringDeserializer;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.kafka.annotation.EnableKafka;
-import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
-import org.springframework.kafka.config.KafkaListenerContainerFactory;
-import org.springframework.kafka.core.ConsumerFactory;
-import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
-import org.springframework.kafka.listener.AbstractMessageListenerContainer;
-import org.springframework.kafka.listener.ConcurrentMessageListenerContainer;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-@Configuration
-@EnableKafka
-public class ReceiverConfig {
-
-    @Value("${kafka.bootstrap-servers}")
-    private String bootstrapServers;
-
-    @Value("${task.group.name}")
-    private String taskGroupName;
-
-    @Bean
-    public Map<String, Object> consumerConfigs() {
-        Map<String, Object> props = new HashMap<>();
-        // list of host:port pairs used for establishing the initial connections to the Kakfa cluster
-        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
-        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
-        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
-        // allows a pool of processes to divide the work of consuming and processing records
-        props.put(ConsumerConfig.GROUP_ID_CONFIG, taskGroupName);
-        props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
-        return props;
-    }
-
-    @Bean
-    public ConsumerFactory<String, String> consumerFactory() {
-        return new DefaultKafkaConsumerFactory<String, String>(consumerConfigs());
-    }
-
-    @Bean
-    public KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, String>> kafkaListenerContainerFactory() {
-        ConcurrentKafkaListenerContainerFactory<String, String> factory =
-                new ConcurrentKafkaListenerContainerFactory<>();
-        factory.setConsumerFactory(consumerFactory());
-        factory.getContainerProperties().setAckMode(AbstractMessageListenerContainer.AckMode.MANUAL_IMMEDIATE);
-        return factory;
-    }
-
-    @Bean
-    public KafkaReceiver receiver() {
-        return new KafkaReceiver();
-    }
-}
\ No newline at end of file
diff --git a/airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/java/org/apache/airavata/k8s/task/job/service/TaskExecutionService.java b/airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/java/org/apache/airavata/k8s/task/job/service/TaskExecutionService.java
deleted file mode 100644
index dfedbd9..0000000
--- a/airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/java/org/apache/airavata/k8s/task/job/service/TaskExecutionService.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/**
- *
- * 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.airavata.k8s.task.job.service;
-
-import org.apache.airavata.k8s.api.resources.compute.ComputeResource;
-import org.apache.airavata.k8s.api.resources.task.TaskParamResource;
-import org.apache.airavata.k8s.api.resources.task.TaskResource;
-import org.apache.airavata.k8s.api.resources.task.TaskStatusResource;
-import org.apache.airavata.k8s.compute.api.ComputeOperations;
-import org.apache.airavata.k8s.compute.api.ExecutionResult;
-import org.apache.airavata.k8s.compute.impl.MockComputeOperation;
-import org.apache.airavata.k8s.compute.impl.SSHComputeOperations;
-import org.apache.airavata.k8s.task.job.messaging.KafkaSender;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-import org.springframework.web.client.RestTemplate;
-
-import java.util.Optional;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/**
- * TODO: Class level comments please
- *
- * @author dimuthu
- * @since 1.0.0-SNAPSHOT
- */
-@Service
-public class TaskExecutionService {
-
-    private final ExecutorService executorService = Executors.newFixedThreadPool(10);
-
-    private final RestTemplate restTemplate;
-    private final KafkaSender kafkaSender;
-
-    @Value("${api.server.url}")
-    private String apiServerUrl;
-
-    @Value("${task.event.topic.name}")
-    private String taskEventPublishTopic;
-
-    public TaskExecutionService(RestTemplate restTemplate, KafkaSender kafkaSender) {
-        this.restTemplate = restTemplate;
-        this.kafkaSender = kafkaSender;
-    }
-
-    public void executeTaskAsync(long taskId) {
-
-        System.out.println("Executing task " + taskId + " as job submission task");
-        TaskResource taskResource = this.restTemplate.getForObject("http://" + apiServerUrl + "/task/" + taskId, TaskResource.class);
-
-        publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.SCHEDULED);
-
-        this.executorService.execute(new Runnable() {
-            @Override
-            public void run() {
-                executeTask(taskResource);
-            }
-        });
-    }
-
-    private void executeTask(TaskResource taskResource) {
-
-        try {
-            Optional<TaskParamResource> commandParam = taskResource.getTaskParams()
-                    .stream()
-                    .filter(taskParamResource -> "command".equals(taskParamResource.getKey()))
-                    .findFirst();
-            Optional<TaskParamResource> argumentsParam = taskResource.getTaskParams()
-                    .stream()
-                    .filter(taskParamResource -> "arguments".equals(taskParamResource.getKey()))
-                    .findFirst();
-            Optional<TaskParamResource> computeId = taskResource.getTaskParams()
-                    .stream()
-                    .filter(taskParamResource -> "compute-id".equals(taskParamResource.getKey()))
-                    .findFirst();
-            Optional<TaskParamResource> experimentDataDir = taskResource.getTaskParams()
-                    .stream()
-                    .filter(taskParamResource -> "exp-data-dir".equals(taskParamResource.getKey()))
-                    .findFirst();
-
-            String processDataDirectory = experimentDataDir
-                    .orElseThrow(() -> new Exception("exp-data-dir param can not be found the tas params of task " +
-                            taskResource.getId())).getValue() + "/" + taskResource.getParentProcessId();
-
-
-            commandParam.ifPresent(taskParamResource -> {
-                try {
-                    String command = taskParamResource.getValue();
-                    command = command.replace("{process-data-dir}", processDataDirectory);
-                    System.out.println("Executing command " + command);
-
-                    argumentsParam.ifPresent(taskArgParamResource -> {
-                        taskArgParamResource.setValue(taskArgParamResource.getValue()
-                                .replace("{process-data-dir}", processDataDirectory));
-                        System.out.println("With arguments " + taskArgParamResource.getValue());
-                    });
-
-                    publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.EXECUTING);
-
-                    ComputeResource computeResource = this.restTemplate.getForObject("http://" + this.apiServerUrl
-                            + "/compute/" + Long.parseLong(computeId.get().getValue()), ComputeResource.class);
-
-                    ComputeOperations operations;
-                    if ("SSH".equals(computeResource.getCommunicationType())) {
-                        operations = new SSHComputeOperations(computeResource.getHost(), computeResource.getUserName(), computeResource.getPassword());
-                    } else if ("Mock".equals(computeResource.getCommunicationType())) {
-                        operations = new MockComputeOperation(computeResource.getHost());
-                    } else {
-                        throw new Exception("No compatible communication method {" + computeResource.getCommunicationType() + "} not found for compute resource " + computeResource.getName());
-                    }
-
-                    ExecutionResult executionResult = operations.executeCommand(command +
-                            (argumentsParam.isPresent() ? argumentsParam.get().getValue() : ""));
-
-                    if (executionResult.getExitStatus() == 0) {
-                        publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.COMPLETED);
-                    } else if (executionResult.getExitStatus() == -1) {
-                        publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED, "Process didn't exit successfully");
-                    } else {
-                        publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED, "Process exited with error status " + executionResult.getExitStatus());
-                    }
-
-                } catch (Exception e) {
-                    e.printStackTrace();
-                    publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED, e.getMessage());
-                }
-            });
-        } catch (Exception e) {
-            e.printStackTrace();
-            publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED, e.getMessage());
-        }
-    }
-
-    public void publishTaskStatus(long processId, long taskId, int status) {
-        publishTaskStatus(processId, taskId, status, "");
-    }
-
-    public void publishTaskStatus(long processId, long taskId, int status, String reason) {
-        this.kafkaSender.send(this.taskEventPublishTopic, processId + "-" + taskId,
-                processId + "," + taskId + "," + status + "," + reason);
-    }
-}
diff --git a/airavata-kubernetes/modules/microservices/workflow-generator/src/main/java/org/apache/airavata/k8s/orchestrator/service/ExperimentLaunchService.java b/airavata-kubernetes/modules/microservices/workflow-generator/src/main/java/org/apache/airavata/k8s/orchestrator/service/ExperimentLaunchService.java
index b67eda8..ee5e591 100644
--- a/airavata-kubernetes/modules/microservices/workflow-generator/src/main/java/org/apache/airavata/k8s/orchestrator/service/ExperimentLaunchService.java
+++ b/airavata-kubernetes/modules/microservices/workflow-generator/src/main/java/org/apache/airavata/k8s/orchestrator/service/ExperimentLaunchService.java
@@ -102,7 +102,7 @@ public class ExperimentLaunchService {
 
         List<TaskResource> taskDag = new ArrayList<>();
 
-        AtomicInteger dagOrder = new AtomicInteger(0);
+        /*AtomicInteger dagOrder = new AtomicInteger(0);
 
         TaskResource dataDirTaskReasource = new TaskResource();
         dataDirTaskReasource.setTaskType(TaskResource.TaskTypes.ENV_SETUP);
@@ -114,7 +114,7 @@ public class ExperimentLaunchService {
                 new TaskParamResource().setKey("compute-id").setValue(computeResource.getId() + ""),
                 new TaskParamResource().setKey("compute-name").setValue(computeResource.getName() + "")));
 
-        dataDirTaskReasource.setOrder(dagOrder.incrementAndGet());
+        dataDirTaskReasource.setReferenceId(dagOrder.incrementAndGet());
         taskDag.add(dataDirTaskReasource);
 
         Optional.ofNullable(appDepRes.getPreJobCommand()).ifPresent(preJob -> {
@@ -127,7 +127,7 @@ public class ExperimentLaunchService {
                     new TaskParamResource().setKey("command").setValue(preJob),
                     new TaskParamResource().setKey("compute-id").setValue(computeResource.getId() + ""),
                     new TaskParamResource().setKey("compute-name").setValue(computeResource.getName() + "")));
-            resource.setOrder(dagOrder.incrementAndGet());
+            resource.setReferenceId(dagOrder.incrementAndGet());
             taskDag.add(resource);
         });
 
@@ -150,7 +150,7 @@ public class ExperimentLaunchService {
                             new TaskParamResource().setKey("target").setValue(localPath),
                             new TaskParamResource().setKey("compute-id").setValue(computeResource.getId() + ""),
                             new TaskParamResource().setKey("compute-name").setValue(computeResource.getName() + "")));
-                    resource.setOrder(dagOrder.incrementAndGet());
+                    resource.setReferenceId(dagOrder.incrementAndGet());
 
                     inputArgument.append(" ");
                     if (expInp.getArguments() != null && !expInp.getArguments().isEmpty()) {
@@ -189,7 +189,7 @@ public class ExperimentLaunchService {
                     new TaskParamResource().setKey("arguments").setValue(inputArgument.toString()),
                     new TaskParamResource().setKey("compute-id").setValue(computeResource.getId() + ""),
                     new TaskParamResource().setKey("compute-name").setValue(computeResource.getName() + "")));
-            resource.setOrder(dagOrder.incrementAndGet());
+            resource.setReferenceId(dagOrder.incrementAndGet());
             taskDag.add(resource);
         });
 
@@ -205,7 +205,7 @@ public class ExperimentLaunchService {
                         new TaskParamResource().setKey("target").setValue(expOut.getId() + ""),
                         new TaskParamResource().setKey("compute-id").setValue(computeResource.getId() + ""),
                         new TaskParamResource().setKey("compute-name").setValue(computeResource.getName() + "")));
-                resource.setOrder(dagOrder.incrementAndGet());
+                resource.setReferenceId(dagOrder.incrementAndGet());
                 taskDag.add(resource);
             }
 
@@ -220,7 +220,7 @@ public class ExperimentLaunchService {
                         new TaskParamResource().setKey("target").setValue(expOut.getId() + ""),
                         new TaskParamResource().setKey("compute-id").setValue(computeResource.getId() + ""),
                         new TaskParamResource().setKey("compute-name").setValue(computeResource.getName() + "")));
-                resource.setOrder(dagOrder.incrementAndGet());
+                resource.setReferenceId(dagOrder.incrementAndGet());
                 taskDag.add(resource);
             }
 
@@ -235,7 +235,7 @@ public class ExperimentLaunchService {
                         new TaskParamResource().setKey("target").setValue(expOut.getId() + ""),
                         new TaskParamResource().setKey("compute-id").setValue(computeResource.getId() + ""),
                         new TaskParamResource().setKey("compute-name").setValue(computeResource.getName() + "")));
-                resource.setOrder(dagOrder.incrementAndGet());
+                resource.setReferenceId(dagOrder.incrementAndGet());
                 taskDag.add(resource);
             }
         }));
@@ -250,9 +250,9 @@ public class ExperimentLaunchService {
                     new TaskParamResource().setKey("command").setValue(postJob),
                     new TaskParamResource().setKey("compute-id").setValue(computeResource.getId() + ""),
                     new TaskParamResource().setKey("compute-name").setValue(computeResource.getName() + "")));
-            resource.setOrder(dagOrder.incrementAndGet());
+            resource.setReferenceId(dagOrder.incrementAndGet());
             taskDag.add(resource);
-        });
+        });*/
 
         return taskDag;
     }
diff --git a/airavata-kubernetes/modules/task-api/pom.xml b/airavata-kubernetes/modules/task-api/pom.xml
new file mode 100644
index 0000000..d2621fc
--- /dev/null
+++ b/airavata-kubernetes/modules/task-api/pom.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>airavata-kubernetes</artifactId>
+        <groupId>org.apache.airavata</groupId>
+        <version>1.0-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>task-api</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.airavata</groupId>
+            <artifactId>api-resource</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.airavata</groupId>
+            <artifactId>compute-resource-api</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-freemarker</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.kafka</groupId>
+            <artifactId>spring-kafka</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.5.1</version>
+                <configuration>
+                    <source>${java.version}</source>
+                    <target>${java.version}</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/AbstractTaskExecutionService.java b/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/AbstractTaskExecutionService.java
new file mode 100644
index 0000000..1f958a7
--- /dev/null
+++ b/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/AbstractTaskExecutionService.java
@@ -0,0 +1,123 @@
+package org.apache.airavata.k8s.task.api;
+
+import org.apache.airavata.k8s.api.resources.compute.ComputeResource;
+import org.apache.airavata.k8s.api.resources.task.TaskInputResource;
+import org.apache.airavata.k8s.api.resources.task.TaskResource;
+import org.apache.airavata.k8s.api.resources.task.TaskStatusResource;
+import org.apache.airavata.k8s.api.resources.task.type.TaskTypeResource;
+import org.apache.airavata.k8s.compute.api.ComputeOperations;
+import org.apache.airavata.k8s.compute.impl.MockComputeOperation;
+import org.apache.airavata.k8s.compute.impl.SSHComputeOperations;
+import org.apache.airavata.k8s.task.api.messaging.KafkaSender;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.PostConstruct;
+import java.util.Optional;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public abstract class AbstractTaskExecutionService {
+
+    private final ExecutorService executorService;
+
+    private final RestTemplate restTemplate;
+    private final KafkaSender kafkaSender;
+
+    @Value("${api.server.url}")
+    private String apiServerUrl;
+
+    @Value("${task.event.topic.name}")
+    private String taskEventPublishTopic;
+
+    public AbstractTaskExecutionService(RestTemplate restTemplate, KafkaSender kafkaSender, int concurrentTasks) {
+        this.restTemplate = restTemplate;
+        this.kafkaSender = kafkaSender;
+        executorService = Executors.newFixedThreadPool(concurrentTasks);
+    }
+
+    @PostConstruct
+    public void init() {
+        getRestTemplate().postForObject("http://" + apiServerUrl + "/taskType", getType(), Long.class);
+    }
+
+    public abstract TaskTypeResource getType();
+
+    public void executeTaskAsync(TaskContext taskContext) {
+
+        System.out.println("Executing task " + taskContext.getTaskId());
+        TaskResource taskResource = this.restTemplate.getForObject("http://" + apiServerUrl + "/task/" + taskContext.getTaskId(), TaskResource.class);
+
+        publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.SCHEDULED);
+
+        this.executorService.execute(() -> {
+            try {
+                initializeParameters(taskResource, taskContext);
+                executeTask(taskResource, taskContext);
+            } catch (Exception e) {
+                e.printStackTrace();
+                // Ignore silently as this is already handled
+                // TODO add a new exception type
+            }
+        });
+    }
+
+    public ComputeOperations fetchComputeResourceOperation(ComputeResource computeResource) throws Exception {
+        ComputeOperations operations;
+        if ("SSH".equals(computeResource.getCommunicationType())) {
+            operations = new SSHComputeOperations(computeResource.getHost(), computeResource.getUserName(), computeResource.getPassword());
+        } else if ("Mock".equals(computeResource.getCommunicationType())) {
+            operations = new MockComputeOperation(computeResource.getHost());
+        } else {
+            throw new Exception("No compatible communication method {" + computeResource.getCommunicationType() + "} not found for compute resource " + computeResource.getName());
+        }
+        return operations;
+    }
+
+    public String findInput(TaskResource taskResource, String name, boolean optional) throws Exception {
+
+        Optional<TaskInputResource> inputResource = taskResource.getInputs()
+                .stream()
+                .filter(input -> name.equals(input.getValue()))
+                .findFirst();
+
+        if (inputResource.isPresent()) {
+            return inputResource.get().getValue();
+
+        } else {
+            if (!optional) {
+                publishTaskStatus(taskResource.getParentProcessId(), taskResource.getId(), TaskStatusResource.State.FAILED,
+                        name + " is not available in inputs");
+                throw new Exception(name + " is not available in inputs");
+            } else {
+                return null;
+            }
+        }
+    }
+
+    public abstract void initializeParameters(TaskResource taskResource, TaskContext taskContext) throws Exception;
+    public abstract void executeTask(TaskResource taskResource, TaskContext taskContext);
+
+    public void publishTaskStatus(long processId, long taskId, int status) {
+        publishTaskStatus(processId, taskId, status, "");
+    }
+
+    public void publishTaskStatus(long processId, long taskId, int status, String reason) {
+        this.kafkaSender.send(this.taskEventPublishTopic, processId + "-" + taskId,
+                processId + "," + taskId + "," + status + "," + reason);
+    }
+
+    public RestTemplate getRestTemplate() {
+        return restTemplate;
+    }
+
+    public String getApiServerUrl() {
+        return apiServerUrl;
+    }
+}
diff --git a/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/TaskContext.java b/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/TaskContext.java
new file mode 100644
index 0000000..2fdf8af
--- /dev/null
+++ b/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/TaskContext.java
@@ -0,0 +1,54 @@
+package org.apache.airavata.k8s.task.api;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public class TaskContext {
+
+    private long taskId;
+    private Map<String, String> contextVariableParams = new HashMap<>();
+    private Map<String, String> contextDataParams = new HashMap<>();
+    private Map<String, Object> localContext = new HashMap<>();
+
+    public long getTaskId() {
+        return taskId;
+    }
+
+    public TaskContext setTaskId(long taskId) {
+        this.taskId = taskId;
+        return this;
+    }
+
+    public Map<String, String> getContextVariableParams() {
+        return contextVariableParams;
+    }
+
+    public TaskContext setContextVariableParams(Map<String, String> contextVariableParams) {
+        this.contextVariableParams = contextVariableParams;
+        return this;
+    }
+
+    public Map<String, String> getContextDataParams() {
+        return contextDataParams;
+    }
+
+    public TaskContext setContextDataParams(Map<String, String> contextDataParams) {
+        this.contextDataParams = contextDataParams;
+        return this;
+    }
+
+    public Map<String, Object> getLocalContext() {
+        return localContext;
+    }
+
+    public TaskContext setLocalContext(Map<String, Object> localContext) {
+        this.localContext = localContext;
+        return this;
+    }
+}
diff --git a/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/TaskContextDeserializer.java b/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/TaskContextDeserializer.java
new file mode 100644
index 0000000..524ce2e
--- /dev/null
+++ b/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/TaskContextDeserializer.java
@@ -0,0 +1,29 @@
+package org.apache.airavata.k8s.task.api;
+
+import org.apache.kafka.common.serialization.Deserializer;
+
+import java.util.Map;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public class TaskContextDeserializer implements Deserializer<TaskContext> {
+
+    @Override
+    public void configure(Map<String, ?> configs, boolean isKey) {
+
+    }
+
+    @Override
+    public TaskContext deserialize(String topic, byte[] data) {
+        return null;
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/TaskContextSerializer.java b/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/TaskContextSerializer.java
new file mode 100644
index 0000000..eb4c762
--- /dev/null
+++ b/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/TaskContextSerializer.java
@@ -0,0 +1,28 @@
+package org.apache.airavata.k8s.task.api;
+
+import org.apache.kafka.common.serialization.Serializer;
+
+import java.util.Map;
+
+/**
+ * TODO: Class level comments please
+ *
+ * @author dimuthu
+ * @since 1.0.0-SNAPSHOT
+ */
+public class TaskContextSerializer implements Serializer<TaskContext> {
+    @Override
+    public void configure(Map<String, ?> configs, boolean isKey) {
+
+    }
+
+    @Override
+    public byte[] serialize(String topic, TaskContext data) {
+        return new byte[0];
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/java/org/apache/airavata/k8s/task/job/messaging/KafkaReceiver.java b/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/messaging/KafkaReceiver.java
similarity index 73%
rename from airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/java/org/apache/airavata/k8s/task/job/messaging/KafkaReceiver.java
rename to airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/messaging/KafkaReceiver.java
index 574c075..c3597dc 100644
--- a/airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/java/org/apache/airavata/k8s/task/job/messaging/KafkaReceiver.java
+++ b/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/messaging/KafkaReceiver.java
@@ -17,9 +17,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.airavata.k8s.task.job.messaging;
+package org.apache.airavata.k8s.task.api.messaging;
 
-import org.apache.airavata.k8s.task.job.service.TaskExecutionService;
+import org.apache.airavata.k8s.task.api.AbstractTaskExecutionService;
+import org.apache.airavata.k8s.task.api.TaskContext;
 import org.springframework.kafka.annotation.KafkaListener;
 import org.springframework.kafka.support.Acknowledgment;
 
@@ -34,12 +35,12 @@ import javax.annotation.Resource;
 public class KafkaReceiver {
 
     @Resource
-    private TaskExecutionService taskExecutionService;
+    private AbstractTaskExecutionService taskExecutionService;
 
     @KafkaListener(topics = "${task.read.topic.name}")
-    public void receiveTasks(String payload, Acknowledgment ack) {
-        System.out.println("received task=" + payload);
-        taskExecutionService.executeTaskAsync(Long.parseLong(payload));
+    public void receiveTasks(TaskContext taskContext, Acknowledgment ack) {
+        System.out.println("received task=" + taskContext.toString());
+        taskExecutionService.executeTaskAsync(taskContext);
         ack.acknowledge();
     }
 }
diff --git a/airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/java/org/apache/airavata/k8s/task/job/messaging/KafkaSender.java b/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/messaging/KafkaSender.java
similarity index 96%
rename from airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/java/org/apache/airavata/k8s/task/job/messaging/KafkaSender.java
rename to airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/messaging/KafkaSender.java
index 395ada1..307215f 100644
--- a/airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/java/org/apache/airavata/k8s/task/job/messaging/KafkaSender.java
+++ b/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/messaging/KafkaSender.java
@@ -17,7 +17,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.airavata.k8s.task.job.messaging;
+package org.apache.airavata.k8s.task.api.messaging;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.kafka.core.KafkaTemplate;
diff --git a/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/java/org/apacher/airavata/k8s/task/egress/messaging/ReceiverConfig.java b/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/messaging/ReceiverConfig.java
similarity index 96%
rename from airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/java/org/apacher/airavata/k8s/task/egress/messaging/ReceiverConfig.java
rename to airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/messaging/ReceiverConfig.java
index bd8ba90..b078a79 100644
--- a/airavata-kubernetes/modules/microservices/tasks/egress-staging-task/src/main/java/org/apacher/airavata/k8s/task/egress/messaging/ReceiverConfig.java
+++ b/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/messaging/ReceiverConfig.java
@@ -17,12 +17,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apacher.airavata.k8s.task.egress.messaging;
+package org.apache.airavata.k8s.task.api.messaging;
 
 import org.apache.kafka.clients.consumer.ConsumerConfig;
 import org.apache.kafka.common.serialization.StringDeserializer;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.kafka.annotation.EnableKafka;
 import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
@@ -42,6 +43,7 @@ import java.util.Map;
  * @since 1.0.0-SNAPSHOT
  */
 @Configuration
+@ComponentScan
 @EnableKafka
 public class ReceiverConfig {
 
diff --git a/airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/java/org/apache/airavata/k8s/task/job/messaging/SenderConfig.java b/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/messaging/SenderConfig.java
similarity index 97%
rename from airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/java/org/apache/airavata/k8s/task/job/messaging/SenderConfig.java
rename to airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/messaging/SenderConfig.java
index 88a4890..cd8b54b 100644
--- a/airavata-kubernetes/modules/microservices/tasks/job-submission-task/src/main/java/org/apache/airavata/k8s/task/job/messaging/SenderConfig.java
+++ b/airavata-kubernetes/modules/task-api/src/main/java/org/apache/airavata/k8s/task/api/messaging/SenderConfig.java
@@ -17,7 +17,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.airavata.k8s.task.job.messaging;
+package org.apache.airavata.k8s.task.api.messaging;
 
 import org.apache.kafka.clients.producer.ProducerConfig;
 import org.apache.kafka.common.serialization.StringSerializer;
diff --git a/airavata-kubernetes/pom.xml b/airavata-kubernetes/pom.xml
index b0ee98f..c758f70 100644
--- a/airavata-kubernetes/pom.xml
+++ b/airavata-kubernetes/pom.xml
@@ -36,11 +36,10 @@
         <module>modules/microservices/workflow-generator</module>
         <module>modules/microservices/task-scheduler</module>
         <module>modules/microservices/event-sink</module>
-        <module>modules/microservices/tasks/ingress-staging-task</module>
-        <module>modules/microservices/tasks/env-setup-task</module>
-        <module>modules/microservices/tasks/job-submission-task</module>
-        <module>modules/microservices/tasks/env-cleanup-task</module>
-        <module>modules/microservices/tasks/egress-staging-task</module>
+        <module>modules/microservices/tasks/command-executing-task</module>
+        <module>modules/microservices/tasks/data-pushing-task</module>
+        <module>modules/microservices/tasks/data-collecting-task</module>
+        <module>modules/task-api</module>
     </modules>
 
     <dependencyManagement>
diff --git a/airavata-kubernetes/web-console/src/app/app.module.ts b/airavata-kubernetes/web-console/src/app/app.module.ts
index 76c7338..6e4d842 100644
--- a/airavata-kubernetes/web-console/src/app/app.module.ts
+++ b/airavata-kubernetes/web-console/src/app/app.module.ts
@@ -15,6 +15,8 @@ import {FormsModule} from "@angular/forms";
 import {ExperimentDetailComponent} from "./components/experiment/detail/experiment.detail";
 import {ProcessDetailComponent} from "./components/process/detail/process.detail.component";
 import {SetupComponent} from "./components/setup/setup.component";
+import {WorkflowCreateComponent} from "./components/workflow/create/create.component";
+import {WorkflowListComponent} from "./components/workflow/list/workflow.list.component";
 
 @NgModule({
   declarations: [
@@ -27,7 +29,9 @@ import {SetupComponent} from "./components/setup/setup.component";
     ExperimentListComponent,
     ExperimentDetailComponent,
     ProcessDetailComponent,
-    SetupComponent
+    SetupComponent,
+    WorkflowCreateComponent,
+    WorkflowListComponent
   ],
   imports: [
     NgbModule.forRoot(),
diff --git a/airavata-kubernetes/web-console/src/app/components/dashboard/dashboard.html b/airavata-kubernetes/web-console/src/app/components/dashboard/dashboard.html
index ba6ab8d..827ca51 100644
--- a/airavata-kubernetes/web-console/src/app/components/dashboard/dashboard.html
+++ b/airavata-kubernetes/web-console/src/app/components/dashboard/dashboard.html
@@ -34,6 +34,9 @@
       <li class="nav-item">
         <a class="nav-link" [routerLink]="['/setup']">Setup</a>
       </li>
+      <li class="nav-item">
+        <a class="nav-link" [routerLink]="['/workflow']">Workflow</a>
+      </li>
     </ul>
   </div>
 
diff --git a/airavata-kubernetes/web-console/src/app/components/dashboard/dashboard.routes.ts b/airavata-kubernetes/web-console/src/app/components/dashboard/dashboard.routes.ts
index 90b4991..c211503 100644
--- a/airavata-kubernetes/web-console/src/app/components/dashboard/dashboard.routes.ts
+++ b/airavata-kubernetes/web-console/src/app/components/dashboard/dashboard.routes.ts
@@ -8,6 +8,8 @@ import {Routes} from "@angular/router";
 import {ExperimentDetailComponent} from "../experiment/detail/experiment.detail";
 import {ProcessDetailComponent} from "../process/detail/process.detail.component";
 import {SetupComponent} from "../setup/setup.component";
+import {WorkflowCreateComponent} from "../workflow/create/create.component";
+import {WorkflowListComponent} from "../workflow/list/workflow.list.component";
 
 /**
  * Created by dimuthu on 10/29/17.
@@ -46,5 +48,13 @@ export const DASHBOARD_ROUTES: Routes = [
   {
     path: 'module',
     component: AppModuleListComponent,
+  },
+  {
+    path: 'workflow',
+    component: WorkflowListComponent,
+  },
+  {
+    path: 'workflow/create',
+    component: WorkflowCreateComponent,
   }
 ];
diff --git a/airavata-kubernetes/web-console/src/app/components/workflow/create/create.component.ts b/airavata-kubernetes/web-console/src/app/components/workflow/create/create.component.ts
new file mode 100644
index 0000000..e286c19
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/app/components/workflow/create/create.component.ts
@@ -0,0 +1,435 @@
+import {Component, ViewEncapsulation, AfterViewInit} from "@angular/core";
+import {TaskService} from "../../../services/task.service";
+import {TaskType} from "../../../models/task/type/task.type.model";
+import {OperationType} from "../../../models/task/type/operation/operation.type.model";
+import {OperationInPort} from "../../../models/task/type/operation/operation.inport.type.model";
+import {OperationOutPort} from "../../../models/task/type/operation/operation.outport.type.model";
+import {WorkflowService} from "../../../services/workflow.service";
+import {Router} from "@angular/router";
+
+declare let mxClient: any;
+declare let mxUtils: any;
+declare let mxToolbar: any;
+declare let mxDivResizer: any;
+declare let mxGraph: any;
+declare let mxDragSource: any;
+declare let mxRubberband: any;
+declare let mxKeyHandler: any;
+declare let mxCodec: any;
+declare let mxConstants: any;
+declare let mxEdgeStyle: any;
+declare let mxEvent: any;
+declare let mxForm: any;
+declare let mxCellAttributeChange: any;
+declare let mxRectangle: any;
+declare let mxPoint: any;
+
+@Component({
+  templateUrl: './create.html',
+  encapsulation: ViewEncapsulation.None,
+  providers: [TaskService, WorkflowService]
+})
+export class WorkflowCreateComponent implements AfterViewInit {
+
+  taskTypes: Array<TaskType> = [];
+  operationTypes: Array<OperationType> = [];
+  graphInstance: any;
+  workFlowName: string = "Sample Workflow";
+
+  constructor(private taskService: TaskService, private workflowService: WorkflowService, private router: Router) {
+
+    let startType: OperationType = new OperationType();
+    startType.id = 1;
+    startType.name = "Start";
+    startType.icon = "assets/icons/start.png";
+    startType.outPorts.push(new OperationOutPort(1, "Output"));
+
+    let endType: OperationType = new OperationType();
+    endType.id = 1;
+    endType.name = "Stop";
+    endType.icon = "assets/icons/stop.png";
+    endType.inPorts.push(new OperationInPort(1, "Input"));
+
+    this.operationTypes.push(startType, endType);
+  }
+
+  loadGraphData() {
+    this.taskService.getAllTaskTypes().subscribe(data => {
+      this.taskTypes = data;
+      this.loadGraph(document.getElementById('graphContainer'), document.getElementById('toolContainer'));
+    }, err => {
+
+    })
+  }
+
+  onShowWorkflowXMLClicked() {
+    let encoder = new mxCodec();
+    let node = encoder.encode(this.graphInstance.getModel());
+    mxUtils.popup(mxUtils.getPrettyXml(node), true);
+  }
+
+  onCreateWorkflowClicked() {
+    let encoder = new mxCodec();
+    let node = encoder.encode(this.graphInstance.getModel());
+    this.workflowService.createWorkflow(this.workFlowName, mxUtils.getPrettyXml(node)).subscribe(data => {
+      alert("Workflow created");
+      this.routeToListPage();
+    }, err => {
+      alert("An error occurred");
+    });
+  }
+
+  routeToListPage() {
+    this.router.navigateByUrl("/workflow");
+  }
+
+  ngAfterViewInit(): void {
+    this.loadGraphData();
+  }
+
+  loadGraph(container, tbContainer) {
+
+    if (!mxClient.isBrowserSupported()) {
+      // Displays an error message if the browser is not supported.
+      mxUtils.error('Browser is not supported!', 200, false);
+    } else {
+      let toolbar = new mxToolbar(tbContainer);
+      toolbar.enabled = false;
+
+      // Workaround for Internet Explorer ignoring certain styles
+      if (mxClient.IS_QUIRKS) {
+        document.body.style.overflow = 'hidden';
+        new mxDivResizer(tbContainer);
+        new mxDivResizer(container);
+      }
+
+      let doc = mxUtils.createXmlDocument();
+
+      let relation = doc.createElement('Edge');
+
+      let graph = new mxGraph(container);
+      this.graphInstance = graph;
+
+      graph.dropEnabled = true;
+
+      mxDragSource.prototype.getDropTarget = function (graph, x, y) {
+        let cell = graph.getCellAt(x, y);
+
+        if (!graph.isValidDropTarget(cell)) {
+          cell = null;
+        }
+
+        return cell;
+      };
+
+      // Enables new connections in the graph
+      graph.setConnectable(true);
+      graph.setMultigraph(false);
+
+      let addTaskType = function (icon, w, h, taskType: TaskType) {
+        addToolbarItem(graph, toolbar, createTaskItem(doc, taskType), icon, doc);
+      };
+
+      let addOperation = function (icon, w, h, operationType: OperationType) {
+        addToolbarItem(graph, toolbar, createOperation(doc, operationType), icon, doc);
+      };
+
+      this.taskTypes.forEach((taskType) => {
+        addTaskType(taskType.icon, 40, 40, taskType);
+      });
+
+      this.operationTypes.forEach((operationType) => {
+        addOperation(operationType.icon, 40, 40, operationType);
+      });
+
+      toolbar.addLine();
+
+      graph.setCellsResizable(false);
+      graph.setResizeContainer(true);
+      graph.minimumContainerSize = new mxRectangle(0, 0, 500, 380);
+      graph.setBorder(60);
+
+      new mxKeyHandler(graph);
+
+      // Overrides method to disallow edge label editing
+      graph.isCellEditable = function (cell) {
+        return !this.getModel().isEdge(cell);
+      };
+
+      // Overrides method to provide a cell label in the display
+      graph.convertValueToString = function (cell) {
+        if (mxUtils.isNode(cell.value)) {
+          if (cell.value.nodeName.toLowerCase() == 'processingelement') {
+            return cell.getAttribute('name', '');
+          }
+          if (cell.value.nodeName.toLowerCase() == 'operation') {
+            return cell.getAttribute('name', '');
+          }
+          else if (cell.value.nodeName.toLowerCase() == 'edge') {
+            return '';
+          }
+
+        }
+        return '';
+      };
+
+      // Overrides method to store a cell label in the model
+      let cellLabelChanged = graph.cellLabelChanged;
+      graph.cellLabelChanged = function (cell, newValue, autoSize) {
+        if (mxUtils.isNode(cell.value) &&
+          cell.value.nodeName.toLowerCase() == 'processingelement') {
+          // Clones the value for correct undo/redo
+          let elt = cell.value.cloneNode(true);
+
+          elt.setAttribute('name', newValue);
+          newValue = elt;
+          autoSize = true;
+        }
+
+        cellLabelChanged.apply(this, arguments);
+      };
+
+      // Overrides method to create the editing value
+      let getEditingValue = graph.getEditingValue;
+      graph.getEditingValue = function (cell) {
+        if (mxUtils.isNode(cell.value) &&
+          cell.value.nodeName.toLowerCase() == 'processingelement') {
+          return cell.getAttribute('name', '');
+        }
+      };
+
+      new mxRubberband(graph);
+
+      // Changes the style for match the markup
+      // Creates the default style for vertices
+      let style = graph.getStylesheet().getDefaultVertexStyle();
+      style[mxConstants.STYLE_STROKECOLOR] = 'gray';
+      style[mxConstants.STYLE_ROUNDED] = true;
+      style[mxConstants.STYLE_SHADOW] = true;
+      style[mxConstants.STYLE_FILLCOLOR] = '#DFDFDF';
+      style[mxConstants.STYLE_GRADIENTCOLOR] = 'white';
+      style[mxConstants.STYLE_FONTCOLOR] = 'black';
+      style[mxConstants.STYLE_FONTSIZE] = '12';
+      style[mxConstants.STYLE_SPACING] = 4;
+
+      // Creates the default style for edges
+      style = graph.getStylesheet().getDefaultEdgeStyle();
+      style[mxConstants.STYLE_STROKECOLOR] = '#0C0C0C';
+      style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR] = 'white';
+      style[mxConstants.STYLE_EDGE] = mxEdgeStyle.ElbowConnector;
+      style[mxConstants.STYLE_ROUNDED] = true;
+      style[mxConstants.STYLE_FONTCOLOR] = 'black';
+      style[mxConstants.STYLE_FONTSIZE] = '10';
+
+      let parent = graph.getDefaultParent();
+      graph.getModel().beginUpdate();
+      try {
+        //var v1 = graph.insertVertex(parent, null, pe1, 40, 40, 80, 30);
+        //var v2 = graph.insertVertex(parent, null, pe2, 200, 150, 80, 30);
+        //var e1 = graph.insertEdge(parent, null, relation, v1, v2);
+      } finally {
+        // Updates the display
+        graph.getModel().endUpdate();
+      }
+
+      // Implements a properties panel that uses
+      // mxCellAttributeChange to change properties
+      graph.getSelectionModel().addListener(mxEvent.CHANGE, function (sender, evt) {
+        selectionChanged(graph);
+      });
+
+      selectionChanged(graph);
+
+    }
+
+    /**
+     * Updates the properties panel
+     */
+    function selectionChanged(graph) {
+      let div = document.getElementById('properties');
+
+      // Forces focusout in IE
+      graph.container.focus();
+
+      // Clears the DIV the non-DOM way
+      div.innerHTML = '';
+
+      // Gets the selection cell
+      let cell = graph.getSelectionCell();
+
+      if (cell == null) {
+        mxUtils.writeln(div, 'Nothing selected.');
+      }
+      else if (cell.value) {
+        // Writes the title
+        let center = document.createElement('center');
+        mxUtils.writeln(center, cell.value.nodeName + ' (' + cell.id + ')');
+        div.appendChild(center);
+        mxUtils.br(div);
+
+        // Creates the form from the attributes of the user object
+        let form = new mxForm();
+
+        let attrs = cell.value.attributes;
+
+        for (let i = 0; i < attrs.length; i++) {
+          if (!attrs[i].nodeName.startsWith("in-") && !attrs[i].nodeName.startsWith("out-")) {
+            createTextField(graph, form, cell, attrs[i]);
+          }
+        }
+
+        div.appendChild(form.getTable());
+        mxUtils.br(div);
+      }
+    }
+
+    function createTaskItem(doc, taskType: TaskType) {
+
+      var pe = doc.createElement('ProcessingElement');
+      pe.setAttribute('name', taskType.name);
+      pe.setAttribute('Type', taskType.id);
+
+      taskType.inputTypes.forEach((input, index) => {
+        pe.setAttribute(input.name, input.defaultValue);
+      });
+
+      taskType.outputTypes.forEach((output, index) => {
+        pe.setAttribute('output-' + output.name, '');
+      });
+
+      taskType.outPorts.forEach((outPort, index) => {
+        pe.setAttribute("out-" + outPort.id, outPort.name);
+      });
+
+      pe.setAttribute("in-1" , "Input");
+
+      return pe;
+    }
+
+    function createOperation(doc, operation: OperationType) {
+      var op = doc.createElement('Operation');
+      op.setAttribute("name", operation.name);
+      operation.outPorts.forEach((outPort, index) => {
+        op.setAttribute("out-" + outPort.id, outPort.name);
+      });
+      operation.inPorts.forEach((inPort, index) => {
+        op.setAttribute("in-" + inPort.id, inPort.name);
+      });
+      return op;
+    }
+
+    function createTextField(graph, form, cell, attribute) {
+      let input = form.addText(attribute.nodeName + ':', attribute.nodeValue);
+
+      let applyHandler = function () {
+        let newValue = input.value || '';
+        let oldValue = cell.getAttribute(attribute.nodeName, '');
+
+        if (newValue != oldValue) {
+          graph.getModel().beginUpdate();
+
+          try {
+            let edit = new mxCellAttributeChange(
+              cell, attribute.nodeName,
+              newValue);
+            graph.getModel().execute(edit);
+            graph.updateCellSize(cell);
+          }
+          finally {
+            graph.getModel().endUpdate();
+          }
+        }
+      };
+
+      mxEvent.addListener(input, 'keypress', function (evt) {
+        // Needs to take shift into account for textareas
+        if (evt.keyCode == /*enter*/13 && !mxEvent.isShiftDown(evt)) {
+          input.blur();
+        }
+      });
+
+      if (mxClient.IS_IE) {
+        mxEvent.addListener(input, 'focusout', applyHandler);
+      }
+      else {
+        // Note: Known problem is the blurring of fields in
+        // Firefox by changing the selection, in which case
+        // no event is fired in FF and the change is lost.
+        // As a workaround you should use a local variable
+        // that stores the focused field and invoke blur
+        // explicitely where we do the graph.focus above.
+        mxEvent.addListener(input, 'blur', applyHandler);
+      }
+    }
+
+    function addToolbarItem(graph, toolbar, prototype, image, doc) {
+      // Function that is executed when the image is dropped on
+      // the graph. The cell argument points to the cell under
+      // the mousepointer if there is one.
+      let funct = function (graph, evt, cell, x, y) {
+
+        let parent = graph.getDefaultParent();
+        let model = graph.getModel();
+
+        let v1 = null;
+
+        model.beginUpdate();
+
+        try {
+          v1 = graph.insertVertex(parent, null, prototype.cloneNode(true), x, y, 80, 60);
+          v1.setConnectable(false);
+
+          let inputs = [];
+          let inputKeys = [];
+          let outputs = [];
+          let outputKeys = [];
+          for (let i = 0; i < prototype.attributes.length; i++) {
+            let attr = prototype.attributes[i];
+            if (attr.nodeName.startsWith("in-")) {
+              inputs.push(attr.nodeValue);
+              inputKeys.push(attr.nodeName)
+            }
+            if (attr.nodeName.startsWith("out-")) {
+              outputs.push(attr.nodeValue);
+              outputKeys.push(attr.nodeName);
+            }
+          }
+
+          let inputDivision = 1 / (inputs.length + 1);
+          let outputDivision = 1 / (outputs.length + 1);
+
+          inputs.forEach(function (input, index) {
+            let port = doc.createElement('InPort');
+            port.setAttribute('name', input);
+            port.setAttribute("port-id", inputKeys[index]);
+
+            let v11 = graph.insertVertex(v1, null, port, 0, (index * inputDivision + inputDivision), 10, 10);
+            v11.geometry.offset = new mxPoint(-5, -5);
+            v11.geometry.relative = true;
+          });
+
+          outputs.forEach(function (output, index) {
+            let port = doc.createElement('OutPort');
+            port.setAttribute('name', output);
+            port.setAttribute("port-id", outputKeys[index]);
+
+            let v11 = graph.insertVertex(v1, null, port, 1, (index * outputDivision + outputDivision), 10, 10);
+            v11.geometry.offset = new mxPoint(-5, -5);
+            v11.geometry.relative = true;
+          });
+        }
+        finally {
+          // Updates the display
+          graph.getModel().endUpdate();
+        }
+
+        graph.updateCellSize(v1);
+        graph.setSelectionCell(v1);
+      };
+
+      // Creates the image which is used as the drag icon (preview)
+      let img = toolbar.addMode(null, image, funct);
+      mxUtils.makeDraggable(img, graph, funct);
+    }
+  };
+}
diff --git a/airavata-kubernetes/web-console/src/app/components/workflow/create/create.html b/airavata-kubernetes/web-console/src/app/components/workflow/create/create.html
new file mode 100644
index 0000000..22723d1
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/app/components/workflow/create/create.html
@@ -0,0 +1,27 @@
+<label for="worklow-name">Workflow Name</label>
+<input [(ngModel)]="workFlowName" name="worklow-name" id="worklow-name"/>
+
+<table style="position:relative;">
+  <tr>
+    <td>
+      <div id="toolContainer" style="border: solid 1px black; width: 80px; cursor: default">
+
+      </div>
+    </td>
+    <td>
+      <div id="graphContainer"
+           style="border: solid 1px black;overflow:hidden;width:321px; cursor:default;">
+      </div>
+    </td>
+    <td valign="top">
+      <div id="properties"
+           style="border: solid 1px black; padding: 10px;">
+      </div>
+    </td>
+  </tr>
+  <tr>
+    <td></td>
+    <td><button (click)="onShowWorkflowXMLClicked()">Show XML</button></td>
+    <td><button (click)="onCreateWorkflowClicked()">Create Workflow</button></td>
+  </tr>
+</table>
diff --git a/airavata-kubernetes/web-console/src/app/components/workflow/list/list.html b/airavata-kubernetes/web-console/src/app/components/workflow/list/list.html
new file mode 100644
index 0000000..7c454dd
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/app/components/workflow/list/list.html
@@ -0,0 +1,23 @@
+<h4>Workflows</h4>
+
+<button class="btn btn-default-outline" (click)="routeToCreatePage()">Add Workflow</button>
+
+<table class="table">
+  <thead class="thead-light">
+
+  <tr>
+    <th scope="col">Id</th>
+    <th scope="col">Name</th>
+    <th scope="col"></th>
+  </tr>
+  </thead>
+
+  <tbody>
+  <tr *ngFor="let workflow of this.workFlows">
+    <th scope="row">{{ workflow.id }}</th>
+    <td>{{ workflow.name }}</td>
+    <td><button (click)="routeToDetailPage(workflow.id)">Detail</button></td>
+  </tr>
+  </tbody>
+
+</table>
diff --git a/airavata-kubernetes/web-console/src/app/components/workflow/list/workflow.list.component.ts b/airavata-kubernetes/web-console/src/app/components/workflow/list/workflow.list.component.ts
new file mode 100644
index 0000000..fc8b9ad
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/app/components/workflow/list/workflow.list.component.ts
@@ -0,0 +1,49 @@
+import {Component, ViewEncapsulation} from "@angular/core";
+import {NgbModal, ModalDismissReasons, NgbModalRef} from '@ng-bootstrap/ng-bootstrap';
+import {ApiService} from "../../../services/api.service";
+import {ExperimentService} from "../../../services/experiment.service";
+import {Experiment} from "../../../models/experiment/experiment.model";
+import {ApplicationIface} from "../../../models/application/application.iface.model";
+import {ApplicationIfaceService} from "../../../services/application.iface.service";
+import {ExperimentInput} from "../../../models/experiment/experiment.input.model";
+import {ExperimentOutput} from "../../../models/experiment/experiment.output.model";
+import {ApplicationDeployment} from "../../../models/application/application.deployment.model";
+import {AppDeploymentService} from "../../../services/deployment.service";
+import {ComputeResource} from "../../../models/compute/compute.resource.model";
+import {ComputeService} from "../../../services/compute.service";
+import {Router} from "@angular/router";
+import {WorkFlow} from "../../../models/workflow/workflow.model";
+import {WorkflowService} from "../../../services/workflow.service";
+
+/**
+ * Created by dimuthu on 10/29/17.
+ */
+
+@Component({
+  templateUrl: './list.html',
+  encapsulation: ViewEncapsulation.None,
+  providers: [WorkflowService]
+})
+export class WorkflowListComponent {
+
+  workFlows: Array<WorkFlow> = [];
+
+  constructor(private workflowService: WorkflowService, private router: Router) {
+    this.getAllWorkFlows();
+  }
+
+  routeToDetailPage(id: number) {
+    this.router.navigateByUrl("/experiment/detail/"+id);
+  }
+
+  routeToCreatePage() {
+    this.router.navigateByUrl("/workflow/create")
+  }
+
+  getAllWorkFlows() {
+    this.workflowService.getAllWorkflows().subscribe(
+      data => {this.workFlows = data},
+      err => {console.log(err);});
+  }
+
+}
diff --git a/airavata-kubernetes/web-console/src/app/models/task/type/operation/operation.inport.type.model.ts b/airavata-kubernetes/web-console/src/app/models/task/type/operation/operation.inport.type.model.ts
new file mode 100644
index 0000000..c52b0f2
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/app/models/task/type/operation/operation.inport.type.model.ts
@@ -0,0 +1,9 @@
+export class OperationInPort {
+  id: number;
+  name: string;
+
+  constructor(id: number, name: string) {
+    this.id = id;
+    this.name = name;
+  }
+}
diff --git a/airavata-kubernetes/web-console/src/app/models/task/type/operation/operation.outport.type.model.ts b/airavata-kubernetes/web-console/src/app/models/task/type/operation/operation.outport.type.model.ts
new file mode 100644
index 0000000..c4a4b38
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/app/models/task/type/operation/operation.outport.type.model.ts
@@ -0,0 +1,9 @@
+export class OperationOutPort {
+  id: number;
+  name: string;
+
+  constructor(id: number, name: string) {
+    this.id = id;
+    this.name = name;
+  }
+}
diff --git a/airavata-kubernetes/web-console/src/app/models/task/type/operation/operation.type.model.ts b/airavata-kubernetes/web-console/src/app/models/task/type/operation/operation.type.model.ts
new file mode 100644
index 0000000..07b7560
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/app/models/task/type/operation/operation.type.model.ts
@@ -0,0 +1,29 @@
+import {OperationInPort} from "./operation.inport.type.model";
+import {OperationOutPort} from "./operation.outport.type.model";
+
+export class OperationType {
+  id: number;
+  name: string;
+  icon: string = "assets/icons/ssh.png";
+  inPorts: Array<OperationInPort> = [];
+  outPorts: Array<OperationOutPort> = [];
+
+  clone(): OperationType {
+    let cloned: OperationType = new OperationType();
+    cloned.id = this.id;
+    cloned.name = this.name;
+    cloned.icon = this.icon;
+
+    this.inPorts.forEach((inPort) => {
+      let clonedInPort: OperationInPort = new OperationInPort(inPort.id, inPort.name);
+      cloned.inPorts.push(clonedInPort);
+    });
+
+    this.outPorts.forEach((outPort) => {
+      let clonedOutPort: OperationOutPort = new OperationOutPort(outPort.id, outPort.name);
+      cloned.outPorts.push(clonedOutPort);
+    });
+
+    return cloned;
+  }
+}
diff --git a/airavata-kubernetes/web-console/src/app/models/task/type/task.input.type.model.ts b/airavata-kubernetes/web-console/src/app/models/task/type/task.input.type.model.ts
new file mode 100644
index 0000000..0f70847
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/app/models/task/type/task.input.type.model.ts
@@ -0,0 +1,14 @@
+export class TaskInputType {
+  id: number;
+  name: String;
+  type: String;
+  defaultValue: string;
+
+
+  constructor(id: number = 0, name: String = null, type: String = null, defaultValue: string = null) {
+    this.id = id;
+    this.name = name;
+    this.type = type;
+    this.defaultValue = defaultValue;
+  }
+}
diff --git a/airavata-kubernetes/web-console/src/app/models/task/type/task.outport.model.ts b/airavata-kubernetes/web-console/src/app/models/task/type/task.outport.model.ts
new file mode 100644
index 0000000..468b0be
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/app/models/task/type/task.outport.model.ts
@@ -0,0 +1,12 @@
+export class TaskOutPort {
+  id: number;
+  name: string;
+  order: number;
+
+
+  constructor(id: number =0 , name: string = null, order: number = 0) {
+    this.id = id;
+    this.name = name;
+    this.order = order;
+  }
+}
diff --git a/airavata-kubernetes/web-console/src/app/models/task/type/task.output.type.model.ts b/airavata-kubernetes/web-console/src/app/models/task/type/task.output.type.model.ts
new file mode 100644
index 0000000..a995603
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/app/models/task/type/task.output.type.model.ts
@@ -0,0 +1,12 @@
+export class TaskOutputType {
+  id: number;
+  name: string;
+  type: string;
+
+
+  constructor(id: number = 0, name: string = null, type: string = null) {
+    this.id = id;
+    this.name = name;
+    this.type = type;
+  }
+}
diff --git a/airavata-kubernetes/web-console/src/app/models/task/type/task.type.model.ts b/airavata-kubernetes/web-console/src/app/models/task/type/task.type.model.ts
new file mode 100644
index 0000000..8572344
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/app/models/task/type/task.type.model.ts
@@ -0,0 +1,39 @@
+import {TaskInputType} from "./task.input.type.model";
+import {TaskOutputType} from "./task.output.type.model";
+import {TaskOutPort} from "./task.outport.model";
+export class TaskType {
+
+  id: number;
+  name: string;
+  topicName: string;
+  icon: string = "assets/icons/ssh.png";
+  inputTypes: Array<TaskInputType> = [];
+  outputTypes: Array<TaskOutputType> = [];
+  outPorts: Array<TaskOutPort> = [];
+
+  clone(): TaskType {
+    let cloned: TaskType = new TaskType();
+    cloned.id = this.id;
+    cloned.name = this.name;
+    cloned.icon = this.icon;
+    cloned.topicName = this.topicName;
+
+    this.inputTypes.forEach((input) => {
+      let clonedInput: TaskInputType = new TaskInputType(input.id, input.name, input.type, input.defaultValue);
+      cloned.inputTypes.push(clonedInput);
+    });
+
+    this.outputTypes.forEach((output) => {
+      let clonedOutput: TaskOutputType = new TaskOutputType(output.id, output.name, output.type);
+      cloned.outputTypes.push(clonedOutput);
+    });
+
+    this.outPorts.forEach((outPort) => {
+      let clonedOutPort: TaskOutPort = new TaskOutPort(outPort.id, outPort.name, outPort.order);
+      cloned.outPorts.push(clonedOutPort);
+    });
+
+    return cloned;
+  }
+
+}
diff --git a/airavata-kubernetes/web-console/src/app/models/workflow/workflow.model.ts b/airavata-kubernetes/web-console/src/app/models/workflow/workflow.model.ts
new file mode 100644
index 0000000..f437c26
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/app/models/workflow/workflow.model.ts
@@ -0,0 +1,8 @@
+export class WorkFlow {
+
+  id: number;
+  name: string;
+  workflowGraphXML: string;
+  processIds: Array<number> = [];
+
+}
diff --git a/airavata-kubernetes/web-console/src/app/services/task.service.ts b/airavata-kubernetes/web-console/src/app/services/task.service.ts
index d7b17ed..f944ab0 100644
--- a/airavata-kubernetes/web-console/src/app/services/task.service.ts
+++ b/airavata-kubernetes/web-console/src/app/services/task.service.ts
@@ -22,4 +22,8 @@ export class TaskService {
   addTask(task: Task) {
     return this.apiService.post("task", task);
   }
+
+  getAllTaskTypes() {
+    return this.apiService.get("taskType").map(res => res.json());
+  }
 }
diff --git a/airavata-kubernetes/web-console/src/app/services/workflow.service.ts b/airavata-kubernetes/web-console/src/app/services/workflow.service.ts
new file mode 100644
index 0000000..f652bd9
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/app/services/workflow.service.ts
@@ -0,0 +1,17 @@
+import {Inject, Injectable} from "@angular/core";
+import {ApiService} from "./api.service";
+
+@Injectable()
+export class WorkflowService {
+
+  constructor(@Inject(ApiService) private apiService:ApiService) {
+  }
+
+  createWorkflow(name: string, xml: any) {
+    return this.apiService.post("workflow/create/" + name, xml)
+  }
+
+  getAllWorkflows() {
+    return this.apiService.get("workflow").map(res => res.json());
+  }
+}
diff --git a/airavata-kubernetes/web-console/src/assets/css/common.css b/airavata-kubernetes/web-console/src/assets/css/common.css
new file mode 100644
index 0000000..1733f6f
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/css/common.css
@@ -0,0 +1,160 @@
+div.mxRubberband {
+	position: absolute;
+	overflow: hidden;
+	border-style: solid;
+	border-width: 1px;
+	border-color: #0000FF;
+	background: #0077FF;
+}
+.mxCellEditor {
+	background: url('../images/transparent.gif');
+	border-color: transparent;
+	border-style: solid;
+	position: absolute;
+	overflow: visible;
+	word-wrap: normal;
+	border-width: 0;
+	min-width: 1px;
+	resize: none;
+	padding: 0px;
+	margin: 0px;
+}
+.mxPlainTextEditor * {
+	padding: 0px;
+	margin: 0px;
+}
+div.mxWindow {
+	-webkit-box-shadow: 3px 3px 12px #C0C0C0;
+	-moz-box-shadow: 3px 3px 12px #C0C0C0;
+	box-shadow: 3px 3px 12px #C0C0C0;
+	background: url('../images/window.gif');
+	border:1px solid #c3c3c3;
+	position: absolute;
+	overflow: hidden;
+	z-index: 1;
+}
+table.mxWindow {
+	border-collapse: collapse;
+	table-layout: fixed;
+  	font-family: Arial;
+	font-size: 8pt;
+}
+td.mxWindowTitle {
+	background: url('../images/window-title.gif') repeat-x;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+ 	text-align: center;
+ 	font-weight: bold;
+ 	overflow: hidden;
+	height: 13px;
+	padding: 2px;
+ 	padding-top: 4px;
+ 	padding-bottom: 6px;
+ 	color: black;
+}
+td.mxWindowPane {
+	vertical-align: top;
+	padding: 0px;
+}
+div.mxWindowPane {
+	overflow: hidden;
+	position: relative;
+}
+td.mxWindowPane td {
+  	font-family: Arial;
+	font-size: 8pt;
+}
+td.mxWindowPane input, td.mxWindowPane select, td.mxWindowPane textarea, td.mxWindowPane radio {
+  	border-color: #8C8C8C;
+  	border-style: solid;
+  	border-width: 1px;
+  	font-family: Arial;
+	font-size: 8pt;
+ 	padding: 1px;
+}
+td.mxWindowPane button {
+	background: url('../images/button.gif') repeat-x;
+  	font-family: Arial;
+  	font-size: 8pt;
+  	padding: 2px;
+	float: left;
+}
+img.mxToolbarItem {
+	margin-right: 6px;
+	margin-bottom: 6px;
+	border-width: 1px;
+}
+select.mxToolbarCombo {
+	vertical-align: top;
+	border-style: inset;
+	border-width: 2px;
+}
+div.mxToolbarComboContainer {
+	padding: 2px;
+}
+img.mxToolbarMode {
+	margin: 2px;
+	margin-right: 4px;
+	margin-bottom: 4px;
+	border-width: 0px;
+}
+img.mxToolbarModeSelected {
+	margin: 0px;
+	margin-right: 2px;
+	margin-bottom: 2px;
+	border-width: 2px;
+	border-style: inset;
+}
+div.mxTooltip {
+	-webkit-box-shadow: 3px 3px 12px #C0C0C0;
+	-moz-box-shadow: 3px 3px 12px #C0C0C0;
+	box-shadow: 3px 3px 12px #C0C0C0;
+	background: #FFFFCC;
+	border-style: solid;
+	border-width: 1px;
+	border-color: black;
+	font-family: Arial;
+	font-size: 8pt;
+	position: absolute;
+	cursor: default;
+	padding: 4px;
+	color: black;
+}
+div.mxPopupMenu {
+	-webkit-box-shadow: 3px 3px 12px #C0C0C0;
+	-moz-box-shadow: 3px 3px 12px #C0C0C0;
+	box-shadow: 3px 3px 12px #C0C0C0;
+	background: url('../images/window.gif');
+	position: absolute;
+	border-style: solid;
+	border-width: 1px;
+	border-color: black;
+}
+table.mxPopupMenu {
+	border-collapse: collapse;
+	margin-top: 1px;
+	margin-bottom: 1px;
+}
+tr.mxPopupMenuItem {
+	color: black;
+	cursor: pointer;
+}
+tr.mxPopupMenuItemHover {
+	background-color: #000066;
+	color: #FFFFFF;
+	cursor: pointer;
+}
+td.mxPopupMenuItem {
+	padding: 2px 30px 2px 10px;
+	white-space: nowrap;
+	font-family: Arial;
+	font-size: 8pt;
+}
+td.mxPopupMenuIcon {
+	background-color: #D0D0D0;
+	padding: 2px 4px 2px 4px;
+}
+.mxDisabled {
+	opacity: 0.2 !important;
+	cursor:default !important;
+}
diff --git a/airavata-kubernetes/web-console/src/assets/css/explorer.css b/airavata-kubernetes/web-console/src/assets/css/explorer.css
new file mode 100644
index 0000000..50e704f
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/css/explorer.css
@@ -0,0 +1,18 @@
+div.mxTooltip {
+	filter:progid:DXImageTransform.Microsoft.DropShadow(OffX=4, OffY=4, 
+        Color='#A2A2A2', Positive='true');
+}
+div.mxPopupMenu {
+	filter:progid:DXImageTransform.Microsoft.DropShadow(OffX=4, OffY=4, 
+        Color='#C0C0C0', Positive='true');
+}
+div.mxWindow {
+	_filter:progid:DXImageTransform.Microsoft.DropShadow(OffX=4, OffY=4, 
+        Color='#C0C0C0', Positive='true');
+}
+td.mxWindowTitle {
+	_height: 23px;
+}
+.mxDisabled {
+	filter:alpha(opacity=20) !important;
+}
diff --git a/airavata-kubernetes/web-console/src/assets/icons/copy.png b/airavata-kubernetes/web-console/src/assets/icons/copy.png
new file mode 100755
index 0000000..b6efb58
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/icons/copy.png differ
diff --git a/airavata-kubernetes/web-console/src/assets/icons/http.png b/airavata-kubernetes/web-console/src/assets/icons/http.png
new file mode 100755
index 0000000..8cbd863
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/icons/http.png differ
diff --git a/airavata-kubernetes/web-console/src/assets/icons/parallel.png b/airavata-kubernetes/web-console/src/assets/icons/parallel.png
new file mode 100755
index 0000000..4e10b7c
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/icons/parallel.png differ
diff --git a/airavata-kubernetes/web-console/src/assets/icons/s3.png b/airavata-kubernetes/web-console/src/assets/icons/s3.png
new file mode 100755
index 0000000..0845fff
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/icons/s3.png differ
diff --git a/airavata-kubernetes/web-console/src/assets/icons/ssh.png b/airavata-kubernetes/web-console/src/assets/icons/ssh.png
new file mode 100755
index 0000000..5e9f91d
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/icons/ssh.png differ
diff --git a/airavata-kubernetes/web-console/src/assets/icons/start.png b/airavata-kubernetes/web-console/src/assets/icons/start.png
new file mode 100755
index 0000000..871fe36
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/icons/start.png differ
diff --git a/airavata-kubernetes/web-console/src/assets/icons/stop.png b/airavata-kubernetes/web-console/src/assets/icons/stop.png
new file mode 100755
index 0000000..6cc9ace
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/icons/stop.png differ
diff --git a/airavata-kubernetes/web-console/src/assets/images/button.gif b/airavata-kubernetes/web-console/src/assets/images/button.gif
new file mode 100644
index 0000000..ad55cab
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/images/button.gif differ
diff --git a/airavata-kubernetes/web-console/src/assets/images/close.gif b/airavata-kubernetes/web-console/src/assets/images/close.gif
new file mode 100644
index 0000000..1069e94
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/images/close.gif differ
diff --git a/airavata-kubernetes/web-console/src/assets/images/collapsed.gif b/airavata-kubernetes/web-console/src/assets/images/collapsed.gif
new file mode 100644
index 0000000..0276444
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/images/collapsed.gif differ
diff --git a/airavata-kubernetes/web-console/src/assets/images/error.gif b/airavata-kubernetes/web-console/src/assets/images/error.gif
new file mode 100644
index 0000000..14e1aee
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/images/error.gif differ
diff --git a/airavata-kubernetes/web-console/src/assets/images/expanded.gif b/airavata-kubernetes/web-console/src/assets/images/expanded.gif
new file mode 100644
index 0000000..3767b0b
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/images/expanded.gif differ
diff --git a/airavata-kubernetes/web-console/src/assets/images/maximize.gif b/airavata-kubernetes/web-console/src/assets/images/maximize.gif
new file mode 100644
index 0000000..e27cf3e
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/images/maximize.gif differ
diff --git a/airavata-kubernetes/web-console/src/assets/images/minimize.gif b/airavata-kubernetes/web-console/src/assets/images/minimize.gif
new file mode 100644
index 0000000..1e95e7c
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/images/minimize.gif differ
diff --git a/airavata-kubernetes/web-console/src/assets/images/normalize.gif b/airavata-kubernetes/web-console/src/assets/images/normalize.gif
new file mode 100644
index 0000000..34a8d30
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/images/normalize.gif differ
diff --git a/airavata-kubernetes/web-console/src/assets/images/point.gif b/airavata-kubernetes/web-console/src/assets/images/point.gif
new file mode 100644
index 0000000..9074c39
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/images/point.gif differ
diff --git a/airavata-kubernetes/web-console/src/assets/images/resize.gif b/airavata-kubernetes/web-console/src/assets/images/resize.gif
new file mode 100644
index 0000000..ff558db
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/images/resize.gif differ
diff --git a/airavata-kubernetes/web-console/src/assets/images/separator.gif b/airavata-kubernetes/web-console/src/assets/images/separator.gif
new file mode 100644
index 0000000..5c1b895
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/images/separator.gif differ
diff --git a/airavata-kubernetes/web-console/src/assets/images/submenu.gif b/airavata-kubernetes/web-console/src/assets/images/submenu.gif
new file mode 100644
index 0000000..ffe7617
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/images/submenu.gif differ
diff --git a/airavata-kubernetes/web-console/src/assets/images/transparent.gif b/airavata-kubernetes/web-console/src/assets/images/transparent.gif
new file mode 100644
index 0000000..76040f2
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/images/transparent.gif differ
diff --git a/airavata-kubernetes/web-console/src/assets/images/warning.gif b/airavata-kubernetes/web-console/src/assets/images/warning.gif
new file mode 100644
index 0000000..705235f
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/images/warning.gif differ
diff --git a/airavata-kubernetes/web-console/src/assets/images/warning.png b/airavata-kubernetes/web-console/src/assets/images/warning.png
new file mode 100644
index 0000000..2f78789
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/images/warning.png differ
diff --git a/airavata-kubernetes/web-console/src/assets/images/window-title.gif b/airavata-kubernetes/web-console/src/assets/images/window-title.gif
new file mode 100644
index 0000000..231def8
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/images/window-title.gif differ
diff --git a/airavata-kubernetes/web-console/src/assets/images/window.gif b/airavata-kubernetes/web-console/src/assets/images/window.gif
new file mode 100644
index 0000000..6631c4f
Binary files /dev/null and b/airavata-kubernetes/web-console/src/assets/images/window.gif differ
diff --git a/airavata-kubernetes/web-console/src/assets/js/components.js b/airavata-kubernetes/web-console/src/assets/js/components.js
new file mode 100644
index 0000000..b3e186f
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/components.js
@@ -0,0 +1,53 @@
+function fetchComponent(name, doc) {
+    if (name == "SSH") {
+        var pe = doc.createElement('ProcessingElement');
+        pe.setAttribute('name', 'SSH Processing element');
+        pe.setAttribute('Type', 'PROCESSING_ELEMENT');
+        pe.setAttribute('Compute-host', '192.168.1.112');
+        pe.setAttribute('User', 'root');
+        pe.setAttribute('Password', 'password');
+        pe.setAttribute('Command', '');
+        pe.setAttribute('Arguments', '');
+        pe.setAttribute("out-1", "Output");
+        pe.setAttribute("out-2", "Error");
+        pe.setAttribute("in-1", "Input");
+        return pe;
+    } else if (name == "CP") {
+        var pe = doc.createElement('ProcessingElement');
+        pe.setAttribute('name', 'Copy Processing element');
+        pe.setAttribute('type', 'PROCESSING_ELEMENT');
+        pe.setAttribute("out-1", "Output");
+        pe.setAttribute("out-2", "Error");
+        pe.setAttribute("in-1", "Input");
+        return pe;
+    } else if (name == "S3") {
+        var pe = doc.createElement('ProcessingElement');
+        pe.setAttribute('name', 'S3 Processing element');
+        pe.setAttribute('type', 'PROCESSING_ELEMENT');
+        pe.setAttribute("out-1", "Output");
+        pe.setAttribute("out-2", "Error");
+        pe.setAttribute("in-1", "Input");
+        return pe;
+    } else if (name == "START") {
+        var pe = doc.createElement('ProcessingElement');
+        pe.setAttribute('name', 'Start Operation');
+        pe.setAttribute('type', 'OPERATION');
+        pe.setAttribute("out-1", "Output");
+        return pe;
+    } else if (name == "STOP") {
+        var pe = doc.createElement('ProcessingElement');
+        pe.setAttribute('name', 'Stop Operation');
+        pe.setAttribute('type', 'OPERATION');
+        pe.setAttribute("in-1", "Input");
+        return pe;
+    } else if (name = "PARALLEL") {
+        var pe = doc.createElement('ProcessingElement');
+        pe.setAttribute('name', 'Parallel Operation');
+        pe.setAttribute('type', 'OPERATION');
+        pe.setAttribute("out-1", "Output1");
+        pe.setAttribute("out-2", "Output2");
+        pe.setAttribute("in-1", "Input");
+        return pe;
+    }
+
+}
\ No newline at end of file
diff --git a/airavata-kubernetes/web-console/src/assets/js/editor/mxDefaultKeyHandler.js b/airavata-kubernetes/web-console/src/assets/js/editor/mxDefaultKeyHandler.js
new file mode 100644
index 0000000..237dea4
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/editor/mxDefaultKeyHandler.js
@@ -0,0 +1,126 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxDefaultKeyHandler
+ *
+ * Binds keycodes to actionnames in an editor. This aggregates an internal
+ * <handler> and extends the implementation of <mxKeyHandler.escape> to not
+ * only cancel the editing, but also hide the properties dialog and fire an
+ * <mxEditor.escape> event via <editor>. An instance of this class is created
+ * by <mxEditor> and stored in <mxEditor.keyHandler>.
+ * 
+ * Example:
+ * 
+ * Bind the delete key to the delete action in an existing editor.
+ * 
+ * (code)
+ * var keyHandler = new mxDefaultKeyHandler(editor);
+ * keyHandler.bindAction(46, 'delete');
+ * (end)
+ *
+ * Codec:
+ * 
+ * This class uses the <mxDefaultKeyHandlerCodec> to read configuration
+ * data into an existing instance. See <mxDefaultKeyHandlerCodec> for a
+ * description of the configuration format.
+ * 
+ * Keycodes:
+ * 
+ * See <mxKeyHandler>.
+ * 
+ * An <mxEvent.ESCAPE> event is fired via the editor if the escape key is
+ * pressed.
+ * 
+ * Constructor: mxDefaultKeyHandler
+ *
+ * Constructs a new default key handler for the <mxEditor.graph> in the
+ * given <mxEditor>. (The editor may be null if a prototypical instance for
+ * a <mxDefaultKeyHandlerCodec> is created.)
+ * 
+ * Parameters:
+ * 
+ * editor - Reference to the enclosing <mxEditor>.
+ */
+function mxDefaultKeyHandler(editor)
+{
+	if (editor != null)
+	{
+		this.editor = editor;
+		this.handler = new mxKeyHandler(editor.graph);
+		
+		// Extends the escape function of the internal key
+		// handle to hide the properties dialog and fire
+		// the escape event via the editor instance
+		var old = this.handler.escape;
+		
+		this.handler.escape = function(evt)
+		{
+			old.apply(this, arguments);
+			editor.hideProperties();
+			editor.fireEvent(new mxEventObject(mxEvent.ESCAPE, 'event', evt));
+		};
+	}
+};
+	
+/**
+ * Variable: editor
+ *
+ * Reference to the enclosing <mxEditor>.
+ */
+mxDefaultKeyHandler.prototype.editor = null;
+
+/**
+ * Variable: handler
+ *
+ * Holds the <mxKeyHandler> for key event handling.
+ */
+mxDefaultKeyHandler.prototype.handler = null;
+
+/**
+ * Function: bindAction
+ *
+ * Binds the specified keycode to the given action in <editor>. The
+ * optional control flag specifies if the control key must be pressed
+ * to trigger the action.
+ *
+ * Parameters:
+ *
+ * code - Integer that specifies the keycode.
+ * action - Name of the action to execute in <editor>.
+ * control - Optional boolean that specifies if control must be pressed.
+ * Default is false.
+ */
+mxDefaultKeyHandler.prototype.bindAction = function (code, action, control)
+{
+	var keyHandler = mxUtils.bind(this, function()
+	{
+		this.editor.execute(action);
+	});
+
+	// Binds the function to control-down keycode
+	if (control)
+	{
+		this.handler.bindControlKey(code, keyHandler);
+	}
+
+	// Binds the function to the normal keycode
+	else
+	{
+		this.handler.bindKey(code, keyHandler);				
+	}
+};
+
+/**
+ * Function: destroy
+ *
+ * Destroys the <handler> associated with this object. This does normally
+ * not need to be called, the <handler> is destroyed automatically when the
+ * window unloads (in IE) by <mxEditor>.
+ */
+mxDefaultKeyHandler.prototype.destroy = function ()
+{
+	this.handler.destroy();
+	this.handler = null;
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/editor/mxDefaultPopupMenu.js b/airavata-kubernetes/web-console/src/assets/js/editor/mxDefaultPopupMenu.js
new file mode 100644
index 0000000..2f2e6e7
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/editor/mxDefaultPopupMenu.js
@@ -0,0 +1,306 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxDefaultPopupMenu
+ *
+ * Creates popupmenus for mouse events. This object holds an XML node
+ * which is a description of the popup menu to be created. In
+ * <createMenu>, the configuration is applied to the context and
+ * the resulting menu items are added to the menu dynamically. See
+ * <createMenu> for a description of the configuration format.
+ * 
+ * This class does not create the DOM nodes required for the popup menu, it
+ * only parses an XML description to invoke the respective methods on an
+ * <mxPopupMenu> each time the menu is displayed.
+ *
+ * Codec:
+ * 
+ * This class uses the <mxDefaultPopupMenuCodec> to read configuration
+ * data into an existing instance, however, the actual parsing is done
+ * by this class during program execution, so the format is described
+ * below.
+ * 
+ * Constructor: mxDefaultPopupMenu
+ *
+ * Constructs a new popupmenu-factory based on given configuration.
+ *
+ * Paramaters:
+ *
+ * config - XML node that contains the configuration data.
+ */
+function mxDefaultPopupMenu(config)
+{
+	this.config = config;
+};
+
+/**
+ * Variable: imageBasePath
+ *
+ * Base path for all icon attributes in the config. Default is null.
+ */
+mxDefaultPopupMenu.prototype.imageBasePath = null;
+
+/**
+ * Variable: config
+ *
+ * XML node used as the description of new menu items. This node is
+ * used in <createMenu> to dynamically create the menu items if their
+ * respective conditions evaluate to true for the given arguments.
+ */
+mxDefaultPopupMenu.prototype.config = null;
+
+/**
+ * Function: createMenu
+ *
+ * This function is called from <mxEditor> to add items to the
+ * given menu based on <config>. The config is a sequence of
+ * the following nodes and attributes.
+ *
+ * Child Nodes: 
+ *
+ * add - Adds a new menu item. See below for attributes.
+ * separator - Adds a separator. No attributes.
+ * condition - Adds a custom condition. Name attribute.
+ * 
+ * The add-node may have a child node that defines a function to be invoked
+ * before the action is executed (or instead of an action to be executed).
+ *
+ * Attributes:
+ *
+ * as - Resource key for the label (needs entry in property file).
+ * action - Name of the action to execute in enclosing editor.
+ * icon - Optional icon (relative/absolute URL).
+ * iconCls - Optional CSS class for the icon.
+ * if - Optional name of condition that must be true (see below).
+ * enabled-if - Optional name of condition that specifies if the menu item
+ * should be enabled.
+ * name - Name of custom condition. Only for condition nodes.
+ *
+ * Conditions:
+ *
+ * nocell - No cell under the mouse.
+ * ncells - More than one cell selected.
+ * notRoot - Drilling position is other than home.
+ * cell - Cell under the mouse.
+ * notEmpty - Exactly one cell with children under mouse.
+ * expandable - Exactly one expandable cell under mouse.
+ * collapsable - Exactly one collapsable cell under mouse.
+ * validRoot - Exactly one cell which is a possible root under mouse.
+ * swimlane - Exactly one cell which is a swimlane under mouse.
+ *
+ * Example:
+ *
+ * To add a new item for a given action to the popupmenu:
+ * 
+ * (code)
+ * <mxDefaultPopupMenu as="popupHandler">
+ *   <add as="delete" action="delete" icon="images/delete.gif" if="cell"/>
+ * </mxDefaultPopupMenu>
+ * (end)
+ * 
+ * To add a new item for a custom function:
+ * 
+ * (code)
+ * <mxDefaultPopupMenu as="popupHandler">
+ *   <add as="action1"><![CDATA[
+ *		function (editor, cell, evt)
+ *		{
+ *			editor.execute('action1', cell, 'myArg');
+ *		}
+ *   ]]></add>
+ * </mxDefaultPopupMenu>
+ * (end)
+ * 
+ * The above example invokes action1 with an additional third argument via
+ * the editor instance. The third argument is passed to the function that
+ * defines action1. If the add-node has no action-attribute, then only the
+ * function defined in the text content is executed, otherwise first the
+ * function and then the action defined in the action-attribute is
+ * executed. The function in the text content has 3 arguments, namely the
+ * <mxEditor> instance, the <mxCell> instance under the mouse, and the
+ * native mouse event.
+ *
+ * Custom Conditions:
+ *
+ * To add a new condition for popupmenu items:
+ *  
+ * (code)
+ * <condition name="condition1"><![CDATA[
+ *   function (editor, cell, evt)
+ *   {
+ *     return cell != null;
+ *   }
+ * ]]></condition>
+ * (end)
+ * 
+ * The new condition can then be used in any item as follows:
+ * 
+ * (code)
+ * <add as="action1" action="action1" icon="action1.gif" if="condition1"/>
+ * (end)
+ * 
+ * The order in which the items and conditions appear is not significant as
+ * all connditions are evaluated before any items are created.
+ * 
+ * Parameters:
+ *
+ * editor - Enclosing <mxEditor> instance.
+ * menu - <mxPopupMenu> that is used for adding items and separators.
+ * cell - Optional <mxCell> which is under the mousepointer.
+ * evt - Optional mouse event which triggered the menu. 
+ */
+mxDefaultPopupMenu.prototype.createMenu = function(editor, menu, cell, evt)
+{
+	if (this.config != null)
+	{
+		var conditions = this.createConditions(editor, cell, evt);
+		var item = this.config.firstChild;
+
+		this.addItems(editor, menu, cell, evt, conditions, item, null);
+	}
+};
+
+/**
+ * Function: addItems
+ * 
+ * Recursively adds the given items and all of its children into the given menu.
+ * 
+ * Parameters:
+ *
+ * editor - Enclosing <mxEditor> instance.
+ * menu - <mxPopupMenu> that is used for adding items and separators.
+ * cell - Optional <mxCell> which is under the mousepointer.
+ * evt - Optional mouse event which triggered the menu.
+ * conditions - Array of names boolean conditions.
+ * item - XML node that represents the current menu item.
+ * parent - DOM node that represents the parent menu item.
+ */
+mxDefaultPopupMenu.prototype.addItems = function(editor, menu, cell, evt, conditions, item, parent)
+{
+	var addSeparator = false;
+	
+	while (item != null)
+	{
+		if (item.nodeName == 'add')
+		{
+			var condition = item.getAttribute('if');
+			
+			if (condition == null || conditions[condition])
+			{
+				var as = item.getAttribute('as');
+				as = mxResources.get(as) || as;
+				var funct = mxUtils.eval(mxUtils.getTextContent(item));
+				var action = item.getAttribute('action');
+				var icon = item.getAttribute('icon');
+				var iconCls = item.getAttribute('iconCls');
+				var enabledCond = item.getAttribute('enabled-if');
+				var enabled = enabledCond == null || conditions[enabledCond];
+				
+				if (addSeparator)
+				{
+					menu.addSeparator(parent);
+					addSeparator = false;
+				}
+				
+				if (icon != null && this.imageBasePath)
+				{
+					icon = this.imageBasePath + icon;
+				}
+				
+				var row = this.addAction(menu, editor, as, icon, funct, action, cell, parent, iconCls, enabled);
+				this.addItems(editor, menu, cell, evt, conditions, item.firstChild, row);
+			}
+		}
+		else if (item.nodeName == 'separator')
+		{
+			addSeparator = true;
+		}
+		
+		item = item.nextSibling;
+	}
+};
+
+/**
+ * Function: addAction
+ *
+ * Helper method to bind an action to a new menu item.
+ * 
+ * Parameters:
+ *
+ * menu - <mxPopupMenu> that is used for adding items and separators.
+ * editor - Enclosing <mxEditor> instance.
+ * lab - String that represents the label of the menu item.
+ * icon - Optional URL that represents the icon of the menu item.
+ * action - Optional name of the action to execute in the given editor.
+ * funct - Optional function to execute before the optional action. The
+ * function takes an <mxEditor>, the <mxCell> under the mouse and the
+ * mouse event that triggered the call.
+ * cell - Optional <mxCell> to use as an argument for the action.
+ * parent - DOM node that represents the parent menu item.
+ * iconCls - Optional CSS class for the menu icon.
+ * enabled - Optional boolean that specifies if the menu item is enabled.
+ * Default is true.
+ */
+mxDefaultPopupMenu.prototype.addAction = function(menu, editor, lab, icon, funct, action, cell, parent, iconCls, enabled)
+{
+	var clickHandler = function(evt)
+	{
+		if (typeof(funct) == 'function')
+		{
+			funct.call(editor, editor, cell, evt);
+		}
+		
+		if (action != null)
+		{
+			editor.execute(action, cell, evt);
+		}
+	};
+	
+	return menu.addItem(lab, icon, clickHandler, parent, iconCls, enabled);
+};
+
+/**
+ * Function: createConditions
+ * 
+ * Evaluates the default conditions for the given context.
+ */
+mxDefaultPopupMenu.prototype.createConditions = function(editor, cell, evt)
+{
+	// Creates array with conditions
+	var model = editor.graph.getModel();
+	var childCount = model.getChildCount(cell);
+	
+	// Adds some frequently used conditions
+	var conditions = [];
+	conditions['nocell'] = cell == null;
+	conditions['ncells'] = editor.graph.getSelectionCount() > 1;
+	conditions['notRoot'] = model.getRoot() !=
+		model.getParent(editor.graph.getDefaultParent());
+	conditions['cell'] = cell != null;
+	
+	var isCell = cell != null && editor.graph.getSelectionCount() == 1;
+	conditions['nonEmpty'] = isCell && childCount > 0;
+	conditions['expandable'] = isCell && editor.graph.isCellFoldable(cell, false);
+	conditions['collapsable'] = isCell && editor.graph.isCellFoldable(cell, true);
+	conditions['validRoot'] = isCell && editor.graph.isValidRoot(cell);
+	conditions['emptyValidRoot'] = conditions['validRoot'] && childCount == 0;
+	conditions['swimlane'] = isCell && editor.graph.isSwimlane(cell);
+
+	// Evaluates dynamic conditions from config file
+	var condNodes = this.config.getElementsByTagName('condition');
+	
+	for (var i=0; i<condNodes.length; i++)
+	{
+		var funct = mxUtils.eval(mxUtils.getTextContent(condNodes[i]));
+		var name = condNodes[i].getAttribute('name');
+		
+		if (name != null && typeof(funct) == 'function')
+		{
+			conditions[name] = funct(editor, cell, evt);
+		}
+	}
+	
+	return conditions;
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/editor/mxDefaultToolbar.js b/airavata-kubernetes/web-console/src/assets/js/editor/mxDefaultToolbar.js
new file mode 100644
index 0000000..8a7f2b6
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/editor/mxDefaultToolbar.js
@@ -0,0 +1,564 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxDefaultToolbar
+ *
+ * Toolbar for the editor. This modifies the state of the graph
+ * or inserts new cells upon mouse clicks.
+ * 
+ * Example:
+ * 
+ * Create a toolbar with a button to copy the selection into the clipboard,
+ * and a combo box with one action to paste the selection from the clipboard
+ * into the graph.
+ * 
+ * (code)
+ * var toolbar = new mxDefaultToolbar(container, editor);
+ * toolbar.addItem('Copy', null, 'copy');
+ * 
+ * var combo = toolbar.addActionCombo('More actions...');
+ * toolbar.addActionOption(combo, 'Paste', 'paste');
+ * (end) 
+ *
+ * Codec:
+ * 
+ * This class uses the <mxDefaultToolbarCodec> to read configuration
+ * data into an existing instance. See <mxDefaultToolbarCodec> for a
+ * description of the configuration format.
+ * 
+ * Constructor: mxDefaultToolbar
+ *
+ * Constructs a new toolbar for the given container and editor. The
+ * container and editor may be null if a prototypical instance for a
+ * <mxDefaultKeyHandlerCodec> is created.
+ *
+ * Parameters:
+ *
+ * container - DOM node that contains the toolbar.
+ * editor - Reference to the enclosing <mxEditor>. 
+ */
+function mxDefaultToolbar(container, editor)
+{
+	this.editor = editor;
+
+	if (container != null && editor != null)
+	{
+		this.init(container);
+	}
+};
+	
+/**
+ * Variable: editor
+ *
+ * Reference to the enclosing <mxEditor>.
+ */
+mxDefaultToolbar.prototype.editor = null;
+
+/**
+ * Variable: toolbar
+ *
+ * Holds the internal <mxToolbar>.
+ */
+mxDefaultToolbar.prototype.toolbar = null;
+
+/**
+ * Variable: resetHandler
+ *
+ * Reference to the function used to reset the <toolbar>.
+ */
+mxDefaultToolbar.prototype.resetHandler = null;
+
+/**
+ * Variable: spacing
+ *
+ * Defines the spacing between existing and new vertices in
+ * gridSize units when a new vertex is dropped on an existing
+ * cell. Default is 4 (40 pixels).
+ */
+mxDefaultToolbar.prototype.spacing = 4;
+
+/**
+ * Variable: connectOnDrop
+ * 
+ * Specifies if elements should be connected if new cells are dropped onto
+ * connectable elements. Default is false.
+ */
+mxDefaultToolbar.prototype.connectOnDrop = false;
+
+/**
+ * Variable: init
+ * 
+ * Constructs the <toolbar> for the given container and installs a listener
+ * that updates the <mxEditor.insertFunction> on <editor> if an item is
+ * selected in the toolbar. This assumes that <editor> is not null.
+ *
+ * Parameters:
+ *
+ * container - DOM node that contains the toolbar.
+ */
+mxDefaultToolbar.prototype.init = function(container)
+{
+	if (container != null)
+	{
+		this.toolbar = new mxToolbar(container);
+		
+		// Installs the insert function in the editor if an item is
+		// selected in the toolbar
+		this.toolbar.addListener(mxEvent.SELECT, mxUtils.bind(this, function(sender, evt)
+		{
+			var funct = evt.getProperty('function');
+			
+			if (funct != null)
+			{
+				this.editor.insertFunction = mxUtils.bind(this, function()
+				{
+					funct.apply(this, arguments);
+					this.toolbar.resetMode();
+				});
+			}
+			else
+			{
+				this.editor.insertFunction = null;
+			}
+		}));
+		
+		// Resets the selected tool after a doubleclick or escape keystroke
+		this.resetHandler = mxUtils.bind(this, function()
+		{
+			if (this.toolbar != null)
+			{
+				this.toolbar.resetMode(true);
+			}
+		});
+
+		this.editor.graph.addListener(mxEvent.DOUBLE_CLICK, this.resetHandler);
+		this.editor.addListener(mxEvent.ESCAPE, this.resetHandler);
+	}
+};
+
+/**
+ * Function: addItem
+ *
+ * Adds a new item that executes the given action in <editor>. The title,
+ * icon and pressedIcon are used to display the toolbar item.
+ * 
+ * Parameters:
+ *
+ * title - String that represents the title (tooltip) for the item.
+ * icon - URL of the icon to be used for displaying the item.
+ * action - Name of the action to execute when the item is clicked.
+ * pressed - Optional URL of the icon for the pressed state.
+ */
+mxDefaultToolbar.prototype.addItem = function(title, icon, action, pressed)
+{
+	var clickHandler = mxUtils.bind(this, function()
+	{
+		if (action != null && action.length > 0)
+		{
+			this.editor.execute(action);
+		}
+	});
+	
+	return this.toolbar.addItem(title, icon, clickHandler, pressed);
+};
+
+/**
+ * Function: addSeparator
+ *
+ * Adds a vertical separator using the optional icon.
+ * 
+ * Parameters:
+ * 
+ * icon - Optional URL of the icon that represents the vertical separator.
+ * Default is <mxClient.imageBasePath> + '/separator.gif'.
+ */
+mxDefaultToolbar.prototype.addSeparator = function(icon)
+{
+	icon = icon || mxClient.imageBasePath + '/separator.gif';
+	this.toolbar.addSeparator(icon);
+};
+	
+/**
+ * Function: addCombo
+ *
+ * Helper method to invoke <mxToolbar.addCombo> on <toolbar> and return the
+ * resulting DOM node.
+ */
+mxDefaultToolbar.prototype.addCombo = function()
+{
+	return this.toolbar.addCombo();
+};
+		
+/**
+ * Function: addActionCombo
+ *
+ * Helper method to invoke <mxToolbar.addActionCombo> on <toolbar> using
+ * the given title and return the resulting DOM node.
+ * 
+ * Parameters:
+ * 
+ * title - String that represents the title of the combo.
+ */
+mxDefaultToolbar.prototype.addActionCombo = function(title)
+{
+	return this.toolbar.addActionCombo(title);
+};
+
+/**
+ * Function: addActionOption
+ *
+ * Binds the given action to a option with the specified label in the
+ * given combo. Combo is an object returned from an earlier call to
+ * <addCombo> or <addActionCombo>.
+ * 
+ * Parameters:
+ * 
+ * combo - DOM node that represents the combo box.
+ * title - String that represents the title of the combo.
+ * action - Name of the action to execute in <editor>.
+ */
+mxDefaultToolbar.prototype.addActionOption = function(combo, title, action)
+{
+	var clickHandler = mxUtils.bind(this, function()
+	{
+		this.editor.execute(action);
+	});
+	
+	this.addOption(combo, title, clickHandler);
+};
+
+/**
+ * Function: addOption
+ *
+ * Helper method to invoke <mxToolbar.addOption> on <toolbar> and return
+ * the resulting DOM node that represents the option.
+ * 
+ * Parameters:
+ * 
+ * combo - DOM node that represents the combo box.
+ * title - String that represents the title of the combo.
+ * value - Object that represents the value of the option.
+ */
+mxDefaultToolbar.prototype.addOption = function(combo, title, value)
+{
+	return this.toolbar.addOption(combo, title, value);
+};
+	
+/**
+ * Function: addMode
+ *
+ * Creates an item for selecting the given mode in the <editor>'s graph.
+ * Supported modenames are select, connect and pan.
+ * 
+ * Parameters:
+ * 
+ * title - String that represents the title of the item.
+ * icon - URL of the icon that represents the item.
+ * mode - String that represents the mode name to be used in
+ * <mxEditor.setMode>.
+ * pressed - Optional URL of the icon that represents the pressed state.
+ * funct - Optional JavaScript function that takes the <mxEditor> as the
+ * first and only argument that is executed after the mode has been
+ * selected.
+ */
+mxDefaultToolbar.prototype.addMode = function(title, icon, mode, pressed, funct)
+{
+	var clickHandler = mxUtils.bind(this, function()
+	{
+		this.editor.setMode(mode);
+		
+		if (funct != null)
+		{
+			funct(this.editor);
+		}
+	});
+	
+	return this.toolbar.addSwitchMode(title, icon, clickHandler, pressed);
+};
+
+/**
+ * Function: addPrototype
+ *
+ * Creates an item for inserting a clone of the specified prototype cell into
+ * the <editor>'s graph. The ptype may either be a cell or a function that
+ * returns a cell.
+ * 
+ * Parameters:
+ * 
+ * title - String that represents the title of the item.
+ * icon - URL of the icon that represents the item.
+ * ptype - Function or object that represents the prototype cell. If ptype
+ * is a function then it is invoked with no arguments to create new
+ * instances.
+ * pressed - Optional URL of the icon that represents the pressed state.
+ * insert - Optional JavaScript function that handles an insert of the new
+ * cell. This function takes the <mxEditor>, new cell to be inserted, mouse
+ * event and optional <mxCell> under the mouse pointer as arguments.
+ * toggle - Optional boolean that specifies if the item can be toggled.
+ * Default is true.
+ */
+mxDefaultToolbar.prototype.addPrototype = function(title, icon, ptype, pressed, insert, toggle)
+{
+	// Creates a wrapper function that is in charge of constructing
+	// the new cell instance to be inserted into the graph
+	var factory = mxUtils.bind(this, function()
+	{
+		if (typeof(ptype) == 'function')
+		{
+			return ptype();
+		}
+		else if (ptype != null)
+		{
+			return this.editor.graph.cloneCells([ptype])[0];
+		}
+		
+		return null;
+	});
+	
+	// Defines the function for a click event on the graph
+	// after this item has been selected in the toolbar
+	var clickHandler = mxUtils.bind(this, function(evt, cell)
+	{
+		if (typeof(insert) == 'function')
+		{
+			insert(this.editor, factory(), evt, cell);
+		}
+		else
+		{
+			this.drop(factory(), evt, cell);
+		}
+		
+		this.toolbar.resetMode();
+		mxEvent.consume(evt);
+	});
+	
+	var img = this.toolbar.addMode(title, icon, clickHandler, pressed, null, toggle);
+				
+	// Creates a wrapper function that calls the click handler without
+	// the graph argument
+	var dropHandler = function(graph, evt, cell)
+	{
+		clickHandler(evt, cell);
+	};
+	
+	this.installDropHandler(img, dropHandler);
+	
+	return img;
+};
+
+/**
+ * Function: drop
+ * 
+ * Handles a drop from a toolbar item to the graph. The given vertex
+ * represents the new cell to be inserted. This invokes <insert> or
+ * <connect> depending on the given target cell.
+ * 
+ * Parameters:
+ * 
+ * vertex - <mxCell> to be inserted.
+ * evt - Mouse event that represents the drop.
+ * target - Optional <mxCell> that represents the drop target.
+ */
+mxDefaultToolbar.prototype.drop = function(vertex, evt, target)
+{
+	var graph = this.editor.graph;
+	var model = graph.getModel();
+	
+	if (target == null ||
+		model.isEdge(target) ||
+		!this.connectOnDrop ||
+		!graph.isCellConnectable(target))
+	{
+		while (target != null &&
+			!graph.isValidDropTarget(target, [vertex], evt))
+		{
+			target = model.getParent(target);
+		}
+		
+		this.insert(vertex, evt, target);
+	}
+	else
+	{
+		this.connect(vertex, evt, target);
+	}
+};
+
+/**
+ * Function: insert
+ *
+ * Handles a drop by inserting the given vertex into the given parent cell
+ * or the default parent if no parent is specified.
+ * 
+ * Parameters:
+ * 
+ * vertex - <mxCell> to be inserted.
+ * evt - Mouse event that represents the drop.
+ * parent - Optional <mxCell> that represents the parent.
+ */
+mxDefaultToolbar.prototype.insert = function(vertex, evt, target)
+{
+	var graph = this.editor.graph;
+	
+	if (graph.canImportCell(vertex))
+	{
+		var x = mxEvent.getClientX(evt);
+		var y = mxEvent.getClientY(evt);
+		var pt = mxUtils.convertPoint(graph.container, x, y);
+		
+		// Splits the target edge or inserts into target group
+		if (graph.isSplitEnabled() &&
+			graph.isSplitTarget(target, [vertex], evt))
+		{
+			return graph.splitEdge(target, [vertex], null, pt.x, pt.y);
+		}
+		else
+		{
+			return this.editor.addVertex(target, vertex, pt.x, pt.y);
+		}
+	}
+	
+	return null;
+};
+
+/**
+ * Function: connect
+ * 
+ * Handles a drop by connecting the given vertex to the given source cell.
+ * 
+ * vertex - <mxCell> to be inserted.
+ * evt - Mouse event that represents the drop.
+ * source - Optional <mxCell> that represents the source terminal.
+ */
+mxDefaultToolbar.prototype.connect = function(vertex, evt, source)
+{
+	var graph = this.editor.graph;
+	var model = graph.getModel();
+	
+	if (source != null &&
+		graph.isCellConnectable(vertex) &&
+		graph.isEdgeValid(null, source, vertex))
+	{
+		var edge = null;
+
+		model.beginUpdate();
+		try
+		{
+			var geo = model.getGeometry(source);
+			var g = model.getGeometry(vertex).clone();
+			
+			// Moves the vertex away from the drop target that will
+			// be used as the source for the new connection
+			g.x = geo.x + (geo.width - g.width) / 2;
+			g.y = geo.y + (geo.height - g.height) / 2;
+			
+			var step = this.spacing * graph.gridSize;
+			var dist = model.getDirectedEdgeCount(source, true) * 20;
+			
+			if (this.editor.horizontalFlow)
+			{
+				g.x += (g.width + geo.width) / 2 + step + dist;
+			}
+			else
+			{
+				g.y += (g.height + geo.height) / 2 + step + dist;
+			}
+			
+			vertex.setGeometry(g);
+			
+			// Fires two add-events with the code below - should be fixed
+			// to only fire one add event for both inserts
+			var parent = model.getParent(source);
+			graph.addCell(vertex, parent);
+			graph.constrainChild(vertex);
+
+			// Creates the edge using the editor instance and calls
+			// the second function that fires an add event
+			edge = this.editor.createEdge(source, vertex);
+			
+			if (model.getGeometry(edge) == null)
+			{
+				var edgeGeometry = new mxGeometry();
+				edgeGeometry.relative = true;
+				
+				model.setGeometry(edge, edgeGeometry);
+			}
+			
+			graph.addEdge(edge, parent, source, vertex);
+		}
+		finally
+		{
+			model.endUpdate();
+		}
+		
+		graph.setSelectionCells([vertex, edge]);
+		graph.scrollCellToVisible(vertex);
+	}
+};
+
+/**
+ * Function: installDropHandler
+ * 
+ * Makes the given img draggable using the given function for handling a
+ * drop event.
+ * 
+ * Parameters:
+ * 
+ * img - DOM node that represents the image.
+ * dropHandler - Function that handles a drop of the image.
+ */
+mxDefaultToolbar.prototype.installDropHandler = function (img, dropHandler)
+{
+	var sprite = document.createElement('img');
+	sprite.setAttribute('src', img.getAttribute('src'));
+
+	// Handles delayed loading of the images
+	var loader = mxUtils.bind(this, function(evt)
+	{
+		// Preview uses the image node with double size. Later this can be
+		// changed to use a separate preview and guides, but for this the
+		// dropHandler must use the additional x- and y-arguments and the
+		// dragsource which makeDraggable returns much be configured to
+		// use guides via mxDragSource.isGuidesEnabled.
+		sprite.style.width = (2 * img.offsetWidth) + 'px';
+		sprite.style.height = (2 * img.offsetHeight) + 'px';
+
+		mxUtils.makeDraggable(img, this.editor.graph, dropHandler,
+			sprite);
+		mxEvent.removeListener(sprite, 'load', loader);
+	});
+
+	if (mxClient.IS_IE)
+	{
+		loader();
+	}
+	else
+	{
+		mxEvent.addListener(sprite, 'load', loader);
+	}	
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the <toolbar> associated with this object and removes all
+ * installed listeners. This does normally not need to be called, the
+ * <toolbar> is destroyed automatically when the window unloads (in IE) by
+ * <mxEditor>.
+ */
+mxDefaultToolbar.prototype.destroy = function ()
+{
+	if (this.resetHandler != null)
+	{
+		this.editor.graph.removeListener('dblclick', this.resetHandler);
+		this.editor.removeListener('escape', this.resetHandler);
+		this.resetHandler = null;
+	}
+	
+	if (this.toolbar != null)
+	{
+		this.toolbar.destroy();
+		this.toolbar = null;
+	}
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/editor/mxEditor.js b/airavata-kubernetes/web-console/src/assets/js/editor/mxEditor.js
new file mode 100644
index 0000000..3aec641
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/editor/mxEditor.js
@@ -0,0 +1,3114 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxEditor
+ *
+ * Extends <mxEventSource> to implement a application wrapper for a graph that
+ * adds <actions>, I/O using <mxCodec>, auto-layout using <mxLayoutManager>,
+ * command history using <undoManager>, and standard dialogs and widgets, eg.
+ * properties, help, outline, toolbar, and popupmenu. It also adds <templates>
+ * to be used as cells in toolbars, auto-validation using the <validation>
+ * flag, attribute cycling using <cycleAttributeValues>, higher-level events
+ * such as <root>, and backend integration using <urlPost> and <urlImage>. 
+ * 
+ * Actions:
+ * 
+ * Actions are functions stored in the <actions> array under their names. The
+ * functions take the <mxEditor> as the first, and an optional <mxCell> as the
+ * second argument and are invoked using <execute>. Any additional arguments
+ * passed to execute are passed on to the action as-is.
+ * 
+ * A list of built-in actions is available in the <addActions> description.
+ * 
+ * Read/write Diagrams:
+ * 
+ * To read a diagram from an XML string, for example from a textfield within the 
+ * page, the following code is used:
+ * 
+ * (code)
+ * var doc = mxUtils.parseXML(xmlString);
+ * var node = doc.documentElement;
+ * editor.readGraphModel(node);
+ * (end)
+ * 
+ * For reading a diagram from a remote location, use the <open> method.
+ * 
+ * To save diagrams in XML on a server, you can set the <urlPost> variable. 
+ * This variable will be used in <getUrlPost> to construct a URL for the post 
+ * request that is issued in the <save> method. The post request contains the 
+ * XML representation of the diagram as returned by <writeGraphModel> in the 
+ * xml parameter.
+ * 
+ * On the server side, the post request is processed using standard
+ * technologies such as Java Servlets, CGI, .NET or ASP.
+ * 
+ * Here are some examples of processing a post request in various languages.
+ * 
+ * - Java: URLDecoder.decode(request.getParameter("xml"), "UTF-8").replace("\n", "&#xa;")
+ * 
+ * Note that the linefeeds should only be replaced if the XML is
+ * processed in Java, for example when creating an image, but not
+ * if the XML is passed back to the client-side.
+ * 
+ * - .NET: HttpUtility.UrlDecode(context.Request.Params["xml"])
+ * - PHP: urldecode($_POST["xml"])
+ * 
+ * Creating images:
+ * 
+ * A backend (Java, PHP or C#) is required for creating images. The
+ * distribution contains an example for each backend (ImageHandler.java,
+ * ImageHandler.cs and graph.php). More information about using a backend
+ * to create images can be found in the readme.html files. Note that the
+ * preview is implemented using VML/SVG in the browser and does not require
+ * a backend. The backend is only required to creates images (bitmaps).
+ * 
+ * Special characters:
+ * 
+ * Note There are five characters that should always appear in XML content as
+ * escapes, so that they do not interact with the syntax of the markup. These
+ * are part of the language for all documents based on XML and for HTML.
+ * 
+ * - &lt; (<)
+ * - &gt; (>)
+ * - &amp; (&)
+ * - &quot; (")
+ * - &apos; (')
+ * 
+ * Although it is part of the XML language, &apos; is not defined in HTML.
+ * For this reason the XHTML specification recommends instead the use of
+ * &#39; if text may be passed to a HTML user agent.
+ * 
+ * If you are having problems with special characters on the server-side then
+ * you may want to try the <escapePostData> flag.
+ * 
+ * For converting decimal escape sequences inside strings, a user has provided
+ * us with the following function:
+ * 
+ * (code)
+ * function html2js(text)
+ * {
+ *   var entitySearch = /&#[0-9]+;/;
+ *   var entity;
+ *   
+ *   while (entity = entitySearch.exec(text))
+ *   {
+ *     var charCode = entity[0].substring(2, entity[0].length -1);
+ *     text = text.substring(0, entity.index)
+ *            + String.fromCharCode(charCode)
+ *            + text.substring(entity.index + entity[0].length);
+ *   }
+ *   
+ *   return text;
+ * }
+ * (end)
+ * 
+ * Otherwise try using hex escape sequences and the built-in unescape function
+ * for converting such strings.
+ * 
+ * Local Files:
+ * 
+ * For saving and opening local files, no standardized method exists that
+ * works across all browsers. The recommended way of dealing with local files
+ * is to create a backend that streams the XML data back to the browser (echo)
+ * as an attachment so that a Save-dialog is displayed on the client-side and
+ * the file can be saved to the local disk.
+ * 
+ * For example, in PHP the code that does this looks as follows.
+ * 
+ * (code)
+ * $xml = stripslashes($_POST["xml"]);
+ * header("Content-Disposition: attachment; filename=\"diagram.xml\"");
+ * echo($xml);
+ * (end)
+ * 
+ * To open a local file, the file should be uploaded via a form in the browser
+ * and then opened from the server in the editor.
+ * 
+ * Cell Properties:
+ * 
+ * The properties displayed in the properties dialog are the attributes and 
+ * values of the cell's user object, which is an XML node. The XML node is 
+ * defined in the templates section of the config file.
+ * 
+ * The templates are stored in <mxEditor.templates> and contain cells which
+ * are cloned at insertion time to create new vertices by use of drag and
+ * drop from the toolbar. Each entry in the toolbar for adding a new vertex
+ * must refer to an existing template.
+ * 
+ * In the following example, the task node is a business object and only the 
+ * mxCell node and its mxGeometry child contain graph information:
+ * 
+ * (code)
+ * <Task label="Task" description="">
+ *   <mxCell vertex="true">
+ *     <mxGeometry as="geometry" width="72" height="32"/>
+ *   </mxCell>
+ * </Task> 
+ * (end)
+ * 
+ * The idea is that the XML representation is inverse from the in-memory 
+ * representation: The outer XML node is the user object and the inner node is 
+ * the cell. This means the user object of the cell is the Task node with no 
+ * children for the above example:
+ * 
+ * (code)
+ * <Task label="Task" description=""/>
+ * (end)
+ * 
+ * The Task node can have any tag name, attributes and child nodes. The 
+ * <mxCodec> will use the XML hierarchy as the user object, while removing the 
+ * "known annotations", such as the mxCell node. At save-time the cell data 
+ * will be "merged" back into the user object. The user object is only modified 
+ * via the properties dialog during the lifecycle of the cell.
+ * 
+ * In the default implementation of <createProperties>, the user object's
+ * attributes are put into a form for editing. Attributes are changed using
+ * the <mxCellAttributeChange> action in the model. The dialog can be replaced 
+ * by overriding the <createProperties> hook or by replacing the showProperties
+ * action in <actions>. Alternatively, the entry in the config file's popupmenu
+ * section can be modified to invoke a different action.
+ * 
+ * If you want to displey the properties dialog on a doubleclick, you can set
+ * <mxEditor.dblClickAction> to showProperties as follows:
+ * 
+ * (code)
+ * editor.dblClickAction = 'showProperties';
+ * (end)
+ * 
+ * Popupmenu and Toolbar:
+ * 
+ * The toolbar and popupmenu are typically configured using the respective
+ * sections in the config file, that is, the popupmenu is defined as follows:
+ * 
+ * (code)
+ * <mxEditor>
+ *   <mxDefaultPopupMenu as="popupHandler">
+ * 		<add as="cut" action="cut" icon="images/cut.gif"/>
+ *      ...
+ * (end)
+ * 
+ * New entries can be added to the toolbar by inserting an add-node into the
+ * above configuration. Existing entries may be removed and changed by
+ * modifying or removing the respective entries in the configuration.
+ * The configuration is read by the <mxDefaultPopupMenuCodec>, the format of the
+ * configuration is explained in <mxDefaultPopupMenu.decode>.
+ * 
+ * The toolbar is defined in the mxDefaultToolbar section. Items can be added
+ * and removed in this section.
+ * 
+ * (code)
+ * <mxEditor>
+ *   <mxDefaultToolbar>
+ *     <add as="save" action="save" icon="images/save.gif"/>
+ *     <add as="Swimlane" template="swimlane" icon="images/swimlane.gif"/>
+ *     ...
+ * (end)
+ * 
+ * The format of the configuration is described in
+ * <mxDefaultToolbarCodec.decode>.
+ * 
+ * Ids:
+ * 
+ * For the IDs, there is an implicit behaviour in <mxCodec>: It moves the Id
+ * from the cell to the user object at encoding time and vice versa at decoding
+ * time. For example, if the Task node from above has an id attribute, then
+ * the <mxCell.id> of the corresponding cell will have this value. If there
+ * is no Id collision in the model, then the cell may be retrieved using this
+ * Id with the <mxGraphModel.getCell> function. If there is a collision, a new
+ * Id will be created for the cell using <mxGraphModel.createId>. At encoding
+ * time, this new Id will replace the value previously stored under the id
+ * attribute in the Task node.
+ * 
+ * See <mxEditorCodec>, <mxDefaultToolbarCodec> and <mxDefaultPopupMenuCodec>
+ * for information about configuring the editor and user interface.
+ * 
+ * Programmatically inserting cells:
+ * 
+ * For inserting a new cell, say, by clicking a button in the document,
+ * the following code can be used. This requires an reference to the editor.
+ * 
+ * (code)
+ * var userObject = new Object();
+ * var parent = editor.graph.getDefaultParent();
+ * var model = editor.graph.model;
+ * model.beginUpdate();
+ * try
+ * {
+ *   editor.graph.insertVertex(parent, null, userObject, 20, 20, 80, 30);
+ * }
+ * finally
+ * {
+ *   model.endUpdate();
+ * }
+ * (end)
+ * 
+ * If a template cell from the config file should be inserted, then a clone
+ * of the template can be created as follows. The clone is then inserted using
+ * the add function instead of addVertex.
+ * 
+ * (code)
+ * var template = editor.templates['task'];
+ * var clone = editor.graph.model.cloneCell(template);
+ * (end)
+ * 
+ * Resources:
+ *
+ * resources/editor - Language resources for mxEditor
+ *
+ * Callback: onInit
+ *
+ * Called from within the constructor. In the callback,
+ * "this" refers to the editor instance.
+ *
+ * Cookie: mxgraph=seen
+ *
+ * Set when the editor is started. Never expires. Use
+ * <resetFirstTime> to reset this cookie. This cookie
+ * only exists if <onInit> is implemented.
+ *
+ * Event: mxEvent.OPEN
+ *
+ * Fires after a file was opened in <open>. The <code>filename</code> property
+ * contains the filename that was used. The same value is also available in
+ * <filename>.
+ *
+ * Event: mxEvent.SAVE
+ *
+ * Fires after the current file was saved in <save>. The <code>url</code>
+ * property contains the URL that was used for saving.
+ *
+ * Event: mxEvent.POST
+ * 
+ * Fires if a successful response was received in <postDiagram>. The
+ * <code>request</code> property contains the <mxXmlRequest>, the
+ * <code>url</code> and <code>data</code> properties contain the URL and the
+ * data that were used in the post request. 
+ *
+ * Event: mxEvent.ROOT
+ *
+ * Fires when the current root has changed, or when the title of the current
+ * root has changed. This event has no properties.
+ *
+ * Event: mxEvent.BEFORE_ADD_VERTEX
+ * 
+ * Fires before a vertex is added in <addVertex>. The <code>vertex</code>
+ * property contains the new vertex and the <code>parent</code> property
+ * contains its parent.
+ * 
+ * Event: mxEvent.ADD_VERTEX
+ * 
+ * Fires between begin- and endUpdate in <addVertex>. The <code>vertex</code>
+ * property contains the vertex that is being inserted.
+ * 
+ * Event: mxEvent.AFTER_ADD_VERTEX
+ * 
+ * Fires after a vertex was inserted and selected in <addVertex>. The
+ * <code>vertex</code> property contains the new vertex.
+ * 
+ * Example:
+ * 
+ * For starting an in-place edit after a new vertex has been added to the
+ * graph, the following code can be used.
+ * 
+ * (code)
+ * editor.addListener(mxEvent.AFTER_ADD_VERTEX, function(sender, evt)
+ * {
+ *   var vertex = evt.getProperty('vertex');
+ * 
+ *   if (editor.graph.isCellEditable(vertex))
+ *   {
+ *   	editor.graph.startEditingAtCell(vertex);
+ *   }
+ * });
+ * (end)
+ * 
+ * Event: mxEvent.ESCAPE
+ * 
+ * Fires when the escape key is pressed. The <code>event</code> property
+ * contains the key event.
+ * 
+ * Constructor: mxEditor
+ *
+ * Constructs a new editor. This function invokes the <onInit> callback
+ * upon completion.
+ *
+ * Example:
+ *
+ * (code)
+ * var config = mxUtils.load('config/diagrameditor.xml').getDocumentElement();
+ * var editor = new mxEditor(config);
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * config - Optional XML node that contains the configuration.
+ */
+function mxEditor(config)
+{
+	this.actions = [];
+	this.addActions();
+
+	// Executes the following only if a document has been instanciated.
+	// That is, don't execute when the editorcodec is setup.
+	if (document.body != null)
+	{
+		// Defines instance fields
+		this.cycleAttributeValues = [];
+		this.popupHandler = new mxDefaultPopupMenu();
+		this.undoManager = new mxUndoManager();
+
+		// Creates the graph and toolbar without the containers
+		this.graph = this.createGraph();
+		this.toolbar = this.createToolbar();
+
+		// Creates the global keyhandler (requires graph instance)
+		this.keyHandler = new mxDefaultKeyHandler(this);
+
+		// Configures the editor using the URI
+		// which was passed to the ctor
+		this.configure(config);
+		
+		// Assigns the swimlaneIndicatorColorAttribute on the graph
+		this.graph.swimlaneIndicatorColorAttribute = this.cycleAttributeName;
+
+		// Checks if the <onInit> hook has been set
+		if (this.onInit != null)
+		{
+			// Invokes the <onInit> hook
+			this.onInit();
+		}
+		
+		// Automatic deallocation of memory
+		if (mxClient.IS_IE)
+		{
+			mxEvent.addListener(window, 'unload', mxUtils.bind(this, function()
+			{
+				this.destroy();
+			}));
+		}
+	}
+};
+
+/**
+ * Installs the required language resources at class
+ * loading time.
+ */
+if (mxLoadResources)
+{
+	mxResources.add(mxClient.basePath+'/resources/editor');
+}
+
+/**
+ * Extends mxEventSource.
+ */
+mxEditor.prototype = new mxEventSource();
+mxEditor.prototype.constructor = mxEditor;
+
+/**
+ * Group: Controls and Handlers
+ */
+	
+/**
+ * Variable: askZoomResource
+ * 
+ * Specifies the resource key for the zoom dialog. If the resource for this
+ * key does not exist then the value is used as the error message. Default
+ * is 'askZoom'.
+ */
+mxEditor.prototype.askZoomResource = (mxClient.language != 'none') ? 'askZoom' : '';
+	
+/**
+ * Variable: lastSavedResource
+ * 
+ * Specifies the resource key for the last saved info. If the resource for
+ * this key does not exist then the value is used as the error message.
+ * Default is 'lastSaved'.
+ */
+mxEditor.prototype.lastSavedResource = (mxClient.language != 'none') ? 'lastSaved' : '';
+	
+/**
+ * Variable: currentFileResource
+ * 
+ * Specifies the resource key for the current file info. If the resource for
+ * this key does not exist then the value is used as the error message.
+ * Default is 'lastSaved'.
+ */
+mxEditor.prototype.currentFileResource = (mxClient.language != 'none') ? 'currentFile' : '';
+	
+/**
+ * Variable: propertiesResource
+ * 
+ * Specifies the resource key for the properties window title. If the
+ * resource for this key does not exist then the value is used as the
+ * error message. Default is 'properties'.
+ */
+mxEditor.prototype.propertiesResource = (mxClient.language != 'none') ? 'properties' : '';
+	
+/**
+ * Variable: tasksResource
+ * 
+ * Specifies the resource key for the tasks window title. If the
+ * resource for this key does not exist then the value is used as the
+ * error message. Default is 'tasks'.
+ */
+mxEditor.prototype.tasksResource = (mxClient.language != 'none') ? 'tasks' : '';
+	
+/**
+ * Variable: helpResource
+ * 
+ * Specifies the resource key for the help window title. If the
+ * resource for this key does not exist then the value is used as the
+ * error message. Default is 'help'.
+ */
+mxEditor.prototype.helpResource = (mxClient.language != 'none') ? 'help' : '';
+	
+/**
+ * Variable: outlineResource
+ * 
+ * Specifies the resource key for the outline window title. If the
+ * resource for this key does not exist then the value is used as the
+ * error message. Default is 'outline'.
+ */
+mxEditor.prototype.outlineResource = (mxClient.language != 'none') ? 'outline' : '';
+	
+/**
+ * Variable: outline
+ * 
+ * Reference to the <mxWindow> that contains the outline. The <mxOutline>
+ * is stored in outline.outline.
+ */
+mxEditor.prototype.outline = null;
+
+/**
+ * Variable: graph
+ *
+ * Holds a <mxGraph> for displaying the diagram. The graph
+ * is created in <setGraphContainer>.
+ */
+mxEditor.prototype.graph = null;
+
+/**
+ * Variable: graphRenderHint
+ *
+ * Holds the render hint used for creating the
+ * graph in <setGraphContainer>. See <mxGraph>.
+ * Default is null.
+ */
+mxEditor.prototype.graphRenderHint = null;
+
+/**
+ * Variable: toolbar
+ *
+ * Holds a <mxDefaultToolbar> for displaying the toolbar. The
+ * toolbar is created in <setToolbarContainer>.
+ */
+mxEditor.prototype.toolbar = null;
+
+/**
+ * Variable: status
+ *
+ * DOM container that holds the statusbar. Default is null.
+ * Use <setStatusContainer> to set this value.
+ */
+mxEditor.prototype.status = null;
+
+/**
+ * Variable: popupHandler
+ *
+ * Holds a <mxDefaultPopupMenu> for displaying
+ * popupmenus.
+ */
+mxEditor.prototype.popupHandler = null;
+
+/**
+ * Variable: undoManager
+ *
+ * Holds an <mxUndoManager> for the command history.
+ */
+mxEditor.prototype.undoManager = null;
+
+/**
+ * Variable: keyHandler
+ *
+ * Holds a <mxDefaultKeyHandler> for handling keyboard events.
+ * The handler is created in <setGraphContainer>.
+ */
+mxEditor.prototype.keyHandler = null;
+
+/**
+ * Group: Actions and Options
+ */
+
+/**
+ * Variable: actions
+ *
+ * Maps from actionnames to actions, which are functions taking
+ * the editor and the cell as arguments. Use <addAction>
+ * to add or replace an action and <execute> to execute an action
+ * by name, passing the cell to be operated upon as the second
+ * argument.
+ */
+mxEditor.prototype.actions = null;
+
+/**
+ * Variable: dblClickAction
+ *
+ * Specifies the name of the action to be executed
+ * when a cell is double clicked. Default is edit.
+ * 
+ * To handle a singleclick, use the following code.
+ * 
+ * (code)
+ * editor.graph.addListener(mxEvent.CLICK, function(sender, evt)
+ * {
+ *   var e = evt.getProperty('event');
+ *   var cell = evt.getProperty('cell');
+ * 
+ *   if (cell != null && !e.isConsumed())
+ *   {
+ *     // Do something useful with cell...
+ *     e.consume();
+ *   }
+ * });
+ * (end)
+ */
+mxEditor.prototype.dblClickAction = 'edit';
+
+/**
+ * Variable: swimlaneRequired
+ * 
+ * Specifies if new cells must be inserted
+ * into an existing swimlane. Otherwise, cells
+ * that are not swimlanes can be inserted as
+ * top-level cells. Default is false.
+ */
+mxEditor.prototype.swimlaneRequired = false;
+
+/**
+ * Variable: disableContextMenu
+ *
+ * Specifies if the context menu should be disabled in the graph container.
+ * Default is true.
+ */
+mxEditor.prototype.disableContextMenu = true;
+
+/**
+ * Group: Templates
+ */
+
+/**
+ * Variable: insertFunction
+ *
+ * Specifies the function to be used for inserting new
+ * cells into the graph. This is assigned from the
+ * <mxDefaultToolbar> if a vertex-tool is clicked.
+ */
+mxEditor.prototype.insertFunction = null;
+
+/**
+ * Variable: forcedInserting
+ *
+ * Specifies if a new cell should be inserted on a single
+ * click even using <insertFunction> if there is a cell 
+ * under the mousepointer, otherwise the cell under the 
+ * mousepointer is selected. Default is false.
+ */
+mxEditor.prototype.forcedInserting = false;
+
+/**
+ * Variable: templates
+ * 
+ * Maps from names to protoype cells to be used
+ * in the toolbar for inserting new cells into
+ * the diagram.
+ */
+mxEditor.prototype.templates = null;
+
+/**
+ * Variable: defaultEdge
+ * 
+ * Prototype edge cell that is used for creating
+ * new edges.
+ */
+mxEditor.prototype.defaultEdge = null;
+
+/**
+ * Variable: defaultEdgeStyle
+ * 
+ * Specifies the edge style to be returned in <getEdgeStyle>.
+ * Default is null.
+ */
+mxEditor.prototype.defaultEdgeStyle = null;
+
+/**
+ * Variable: defaultGroup
+ * 
+ * Prototype group cell that is used for creating
+ * new groups.
+ */
+mxEditor.prototype.defaultGroup = null;
+
+/**
+ * Variable: graphRenderHint
+ *
+ * Default size for the border of new groups. If null,
+ * then then <mxGraph.gridSize> is used. Default is
+ * null.
+ */
+mxEditor.prototype.groupBorderSize = null;
+
+/**
+ * Group: Backend Integration
+ */
+
+/**
+ * Variable: filename
+ *
+ * Contains the URL of the last opened file as a string.
+ * Default is null.
+ */
+mxEditor.prototype.filename = null;
+
+/**
+ * Variable: lineFeed
+ *
+ * Character to be used for encoding linefeeds in <save>. Default is '&#xa;'.
+ */
+mxEditor.prototype.linefeed = '&#xa;';
+
+/**
+ * Variable: postParameterName
+ *
+ * Specifies if the name of the post parameter that contains the diagram
+ * data in a post request to the server. Default is xml.
+ */
+mxEditor.prototype.postParameterName = 'xml';
+
+/**
+ * Variable: escapePostData
+ *
+ * Specifies if the data in the post request for saving a diagram
+ * should be converted using encodeURIComponent. Default is true.
+ */
+mxEditor.prototype.escapePostData = true;
+
+/**
+ * Variable: urlPost
+ *
+ * Specifies the URL to be used for posting the diagram
+ * to a backend in <save>.
+ */
+mxEditor.prototype.urlPost = null;
+
+/**
+ * Variable: urlImage
+ *
+ * Specifies the URL to be used for creating a bitmap of
+ * the graph in the image action.
+ */
+mxEditor.prototype.urlImage = null;
+
+/**
+ * Group: Autolayout
+ */
+
+/**
+ * Variable: horizontalFlow
+ *
+ * Specifies the direction of the flow
+ * in the diagram. This is used in the
+ * layout algorithms. Default is false,
+ * ie. vertical flow.
+ */
+mxEditor.prototype.horizontalFlow = false;
+
+/**
+ * Variable: layoutDiagram
+ *
+ * Specifies if the top-level elements in the
+ * diagram should be layed out using a vertical
+ * or horizontal stack depending on the setting
+ * of <horizontalFlow>. The spacing between the
+ * swimlanes is specified by <swimlaneSpacing>.
+ * Default is false.
+ * 
+ * If the top-level elements are swimlanes, then
+ * the intra-swimlane layout is activated by
+ * the <layoutSwimlanes> switch.
+ */
+mxEditor.prototype.layoutDiagram = false;
+
+/**
+ * Variable: swimlaneSpacing
+ *
+ * Specifies the spacing between swimlanes if
+ * automatic layout is turned on in
+ * <layoutDiagram>. Default is 0.
+ */
+mxEditor.prototype.swimlaneSpacing = 0;
+
+/**
+ * Variable: maintainSwimlanes
+ * 
+ * Specifies if the swimlanes should be kept at the same
+ * width or height depending on the setting of
+ * <horizontalFlow>.  Default is false.
+ * 
+ * For horizontal flows, all swimlanes
+ * have the same height and for vertical flows, all swimlanes
+ * have the same width. Furthermore, the swimlanes are
+ * automatically "stacked" if <layoutDiagram> is true.
+ */
+mxEditor.prototype.maintainSwimlanes = false;
+
+/**
+ * Variable: layoutSwimlanes
+ *
+ * Specifies if the children of swimlanes should
+ * be layed out, either vertically or horizontally
+ * depending on <horizontalFlow>.
+ * Default is false.
+ */
+mxEditor.prototype.layoutSwimlanes = false;
+
+/**
+ * Group: Attribute Cycling
+ */
+ 
+/**
+ * Variable: cycleAttributeValues
+ * 
+ * Specifies the attribute values to be cycled when
+ * inserting new swimlanes. Default is an empty
+ * array.
+ */
+mxEditor.prototype.cycleAttributeValues = null;
+
+/**
+ * Variable: cycleAttributeIndex
+ * 
+ * Index of the last consumed attribute index. If a new
+ * swimlane is inserted, then the <cycleAttributeValues>
+ * at this index will be used as the value for
+ * <cycleAttributeName>. Default is 0.
+ */
+mxEditor.prototype.cycleAttributeIndex = 0;
+
+/**
+ * Variable: cycleAttributeName
+ * 
+ * Name of the attribute to be assigned a <cycleAttributeValues>
+ * when inserting new swimlanes. Default is fillColor.
+ */
+mxEditor.prototype.cycleAttributeName = 'fillColor';
+
+/**
+ * Group: Windows
+ */
+
+/**
+ * Variable: tasks
+ * 
+ * Holds the <mxWindow> created in <showTasks>.
+ */
+mxEditor.prototype.tasks = null;
+
+/**
+ * Variable: tasksWindowImage
+ *
+ * Icon for the tasks window.
+ */
+mxEditor.prototype.tasksWindowImage = null;
+
+/**
+ * Variable: tasksTop
+ * 
+ * Specifies the top coordinate of the tasks window in pixels.
+ * Default is 20.
+ */
+mxEditor.prototype.tasksTop = 20;
+
+/**
+ * Variable: help
+ * 
+ * Holds the <mxWindow> created in <showHelp>.
+ */
+mxEditor.prototype.help = null;
+
+/**
+ * Variable: helpWindowImage
+ *
+ * Icon for the help window.
+ */
+mxEditor.prototype.helpWindowImage = null;
+
+/**
+ * Variable: urlHelp
+ *
+ * Specifies the URL to be used for the contents of the
+ * Online Help window. This is usually specified in the
+ * resources file under urlHelp for language-specific
+ * online help support.
+ */
+mxEditor.prototype.urlHelp = null;
+
+/**
+ * Variable: helpWidth
+ * 
+ * Specifies the width of the help window in pixels.
+ * Default is 300.
+ */
+mxEditor.prototype.helpWidth = 300;
+	
+/**
+ * Variable: helpWidth
+ * 
+ * Specifies the width of the help window in pixels.
+ * Default is 260.
+ */
+mxEditor.prototype.helpHeight = 260;
+
+/**
+ * Variable: propertiesWidth
+ * 
+ * Specifies the width of the properties window in pixels.
+ * Default is 240.
+ */
+mxEditor.prototype.propertiesWidth = 240;
+		
+/**
+ * Variable: propertiesHeight
+ * 
+ * Specifies the height of the properties window in pixels.
+ * If no height is specified then the window will be automatically
+ * sized to fit its contents. Default is null.
+ */
+mxEditor.prototype.propertiesHeight = null;
+		
+/**
+ * Variable: movePropertiesDialog
+ *
+ * Specifies if the properties dialog should be automatically
+ * moved near the cell it is displayed for, otherwise the
+ * dialog is not moved. This value is only taken into 
+ * account if the dialog is already visible. Default is false.
+ */
+mxEditor.prototype.movePropertiesDialog = false;
+
+/**
+ * Variable: validating
+ *
+ * Specifies if <mxGraph.validateGraph> should automatically be invoked after
+ * each change. Default is false.
+ */
+mxEditor.prototype.validating = false;
+
+/**
+ * Variable: modified
+ *
+ * True if the graph has been modified since it was last saved.
+ */
+mxEditor.prototype.modified = false;
+
+/**
+ * Function: isModified
+ * 
+ * Returns <modified>.
+ */
+mxEditor.prototype.isModified = function ()
+{
+	return this.modified;
+};
+
+/**
+ * Function: setModified
+ * 
+ * Sets <modified> to the specified boolean value.
+ */
+mxEditor.prototype.setModified = function (value)
+{
+	this.modified = value;
+};
+
+/**
+ * Function: addActions
+ *
+ * Adds the built-in actions to the editor instance.
+ *
+ * save - Saves the graph using <urlPost>.
+ * print - Shows the graph in a new print preview window.
+ * show - Shows the graph in a new window.
+ * exportImage - Shows the graph as a bitmap image using <getUrlImage>.
+ * refresh - Refreshes the graph's display.
+ * cut - Copies the current selection into the clipboard
+ * and removes it from the graph.
+ * copy - Copies the current selection into the clipboard.
+ * paste - Pastes the clipboard into the graph.
+ * delete - Removes the current selection from the graph.
+ * group - Puts the current selection into a new group.
+ * ungroup - Removes the selected groups and selects the children.
+ * undo - Undoes the last change on the graph model.
+ * redo - Redoes the last change on the graph model.
+ * zoom - Sets the zoom via a dialog.
+ * zoomIn - Zooms into the graph.
+ * zoomOut - Zooms out of the graph
+ * actualSize - Resets the scale and translation on the graph.
+ * fit - Changes the scale so that the graph fits into the window.
+ * showProperties - Shows the properties dialog.
+ * selectAll - Selects all cells.
+ * selectNone - Clears the selection.
+ * selectVertices - Selects all vertices.
+ * selectEdges = Selects all edges.
+ * edit - Starts editing the current selection cell.
+ * enterGroup - Drills down into the current selection cell.
+ * exitGroup - Moves up in the drilling hierachy
+ * home - Moves to the topmost parent in the drilling hierarchy
+ * selectPrevious - Selects the previous cell.
+ * selectNext - Selects the next cell.
+ * selectParent - Selects the parent of the selection cell.
+ * selectChild - Selects the first child of the selection cell.
+ * collapse - Collapses the currently selected cells.
+ * expand - Expands the currently selected cells.
+ * bold - Toggle bold text style.
+ * italic - Toggle italic text style.
+ * underline - Toggle underline text style.
+ * alignCellsLeft - Aligns the selection cells at the left.
+ * alignCellsCenter - Aligns the selection cells in the center.
+ * alignCellsRight - Aligns the selection cells at the right.
+ * alignCellsTop - Aligns the selection cells at the top.
+ * alignCellsMiddle - Aligns the selection cells in the middle.
+ * alignCellsBottom - Aligns the selection cells at the bottom.
+ * alignFontLeft - Sets the horizontal text alignment to left.
+ * alignFontCenter - Sets the horizontal text alignment to center.
+ * alignFontRight - Sets the horizontal text alignment to right.
+ * alignFontTop - Sets the vertical text alignment to top.
+ * alignFontMiddle - Sets the vertical text alignment to middle.
+ * alignFontBottom - Sets the vertical text alignment to bottom.
+ * toggleTasks - Shows or hides the tasks window.
+ * toggleHelp - Shows or hides the help window.
+ * toggleOutline - Shows or hides the outline window.
+ * toggleConsole - Shows or hides the console window.
+ */
+mxEditor.prototype.addActions = function ()
+{
+	this.addAction('save', function(editor)
+	{
+		editor.save();
+	});
+	
+	this.addAction('print', function(editor)
+	{
+		var preview = new mxPrintPreview(editor.graph, 1);
+		preview.open();
+	});
+	
+	this.addAction('show', function(editor)
+	{
+		mxUtils.show(editor.graph, null, 10, 10);
+	});
+
+	this.addAction('exportImage', function(editor)
+	{
+		var url = editor.getUrlImage();
+		
+		if (url == null || mxClient.IS_LOCAL)
+		{
+			editor.execute('show');
+		}
+		else
+		{
+			var node = mxUtils.getViewXml(editor.graph, 1);
+			var xml = mxUtils.getXml(node, '\n');
+
+			mxUtils.submit(url, editor.postParameterName + '=' +
+				encodeURIComponent(xml), document, '_blank');
+		}
+	});
+	
+	this.addAction('refresh', function(editor)
+	{
+		editor.graph.refresh();
+	});
+	
+	this.addAction('cut', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			mxClipboard.cut(editor.graph);
+		}
+	});
+	
+	this.addAction('copy', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			mxClipboard.copy(editor.graph);
+		}
+	});
+	
+	this.addAction('paste', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			mxClipboard.paste(editor.graph);
+		}
+	});
+	
+	this.addAction('delete', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.removeCells();
+		}
+	});
+	
+	this.addAction('group', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setSelectionCell(editor.groupCells());
+		}
+	});
+	
+	this.addAction('ungroup', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setSelectionCells(editor.graph.ungroupCells());
+		}
+	});
+	
+	this.addAction('removeFromParent', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.removeCellsFromParent();
+		}
+	});
+	
+	this.addAction('undo', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.undo();
+		}
+	});
+	
+	this.addAction('redo', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.redo();
+		}
+	});
+	
+	this.addAction('zoomIn', function(editor)
+	{
+		editor.graph.zoomIn();
+	});
+	
+	this.addAction('zoomOut', function(editor)
+	{
+		editor.graph.zoomOut();
+	});
+	
+	this.addAction('actualSize', function(editor)
+	{
+		editor.graph.zoomActual();
+	});
+	
+	this.addAction('fit', function(editor)
+	{
+		editor.graph.fit();
+	});
+	
+	this.addAction('showProperties', function(editor, cell)
+	{
+		editor.showProperties(cell);
+	});
+	
+	this.addAction('selectAll', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectAll();
+		}
+	});
+	
+	this.addAction('selectNone', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.clearSelection();
+		}
+	});
+	
+	this.addAction('selectVertices', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectVertices();
+		}
+	});
+	
+	this.addAction('selectEdges', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectEdges();
+		}
+	});
+	
+	this.addAction('edit', function(editor, cell)
+	{
+		if (editor.graph.isEnabled() &&
+			editor.graph.isCellEditable(cell))
+		{
+			editor.graph.startEditingAtCell(cell);
+		}
+	});
+	
+	this.addAction('toBack', function(editor, cell)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.orderCells(true);
+		}
+	});
+	
+	this.addAction('toFront', function(editor, cell)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.orderCells(false);
+		}
+	});
+	
+	this.addAction('enterGroup', function(editor, cell)
+	{
+		editor.graph.enterGroup(cell);
+	});
+	
+	this.addAction('exitGroup', function(editor)
+	{
+		editor.graph.exitGroup();
+	});
+	
+	this.addAction('home', function(editor)
+	{
+		editor.graph.home();
+	});
+	
+	this.addAction('selectPrevious', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectPreviousCell();
+		}
+	});
+	
+	this.addAction('selectNext', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectNextCell();
+		}
+	});
+	
+	this.addAction('selectParent', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectParentCell();
+		}
+	});
+	
+	this.addAction('selectChild', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.selectChildCell();
+		}
+	});
+	
+	this.addAction('collapse', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.foldCells(true);
+		}
+	});
+	
+	this.addAction('collapseAll', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			var cells = editor.graph.getChildVertices();
+			editor.graph.foldCells(true, false, cells);
+		}
+	});
+	
+	this.addAction('expand', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.foldCells(false);
+		}
+	});
+	
+	this.addAction('expandAll', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			var cells = editor.graph.getChildVertices();
+			editor.graph.foldCells(false, false, cells);
+		}
+	});
+	
+	this.addAction('bold', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.toggleCellStyleFlags(
+				mxConstants.STYLE_FONTSTYLE,
+				mxConstants.FONT_BOLD);
+		}
+	});
+	
+	this.addAction('italic', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.toggleCellStyleFlags(
+				mxConstants.STYLE_FONTSTYLE,
+				mxConstants.FONT_ITALIC);
+		}
+	});
+	
+	this.addAction('underline', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.toggleCellStyleFlags(
+				mxConstants.STYLE_FONTSTYLE,
+				mxConstants.FONT_UNDERLINE);
+		}
+	});
+
+	this.addAction('alignCellsLeft', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.alignCells(mxConstants.ALIGN_LEFT);
+		}
+	});
+	
+	this.addAction('alignCellsCenter', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.alignCells(mxConstants.ALIGN_CENTER);
+		}
+	});
+	
+	this.addAction('alignCellsRight', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.alignCells(mxConstants.ALIGN_RIGHT);
+		}
+	});
+	
+	this.addAction('alignCellsTop', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.alignCells(mxConstants.ALIGN_TOP);
+		}
+	});
+	
+	this.addAction('alignCellsMiddle', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.alignCells(mxConstants.ALIGN_MIDDLE);
+		}
+	});
+	
+	this.addAction('alignCellsBottom', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.alignCells(mxConstants.ALIGN_BOTTOM);
+		}
+	});
+	
+	this.addAction('alignFontLeft', function(editor)
+	{
+		
+		editor.graph.setCellStyles(
+			mxConstants.STYLE_ALIGN,
+			mxConstants.ALIGN_LEFT);
+	});
+	
+	this.addAction('alignFontCenter', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setCellStyles(
+				mxConstants.STYLE_ALIGN,
+				mxConstants.ALIGN_CENTER);
+		}
+	});
+	
+	this.addAction('alignFontRight', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setCellStyles(
+				mxConstants.STYLE_ALIGN,
+				mxConstants.ALIGN_RIGHT);
+		}
+	});
+	
+	this.addAction('alignFontTop', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setCellStyles(
+				mxConstants.STYLE_VERTICAL_ALIGN,
+				mxConstants.ALIGN_TOP);
+		}
+	});
+	
+	this.addAction('alignFontMiddle', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setCellStyles(
+				mxConstants.STYLE_VERTICAL_ALIGN,
+				mxConstants.ALIGN_MIDDLE);
+		}
+	});
+	
+	this.addAction('alignFontBottom', function(editor)
+	{
+		if (editor.graph.isEnabled())
+		{
+			editor.graph.setCellStyles(
+				mxConstants.STYLE_VERTICAL_ALIGN,
+				mxConstants.ALIGN_BOTTOM);
+		}
+	});
+	
+	this.addAction('zoom', function(editor)
+	{
+		var current = editor.graph.getView().scale*100;
+		var scale = parseFloat(mxUtils.prompt(
+			mxResources.get(editor.askZoomResource) ||
+			editor.askZoomResource,
+			current))/100;
+
+		if (!isNaN(scale))
+		{
+			editor.graph.getView().setScale(scale);
+		}
+	});
+	
+	this.addAction('toggleTasks', function(editor)
+	{
+		if (editor.tasks != null)
+		{
+			editor.tasks.setVisible(!editor.tasks.isVisible());
+		}
+		else
+		{
+			editor.showTasks();
+		}
+	});
+	
+	this.addAction('toggleHelp', function(editor)
+	{
+		if (editor.help != null)
+		{
+			editor.help.setVisible(!editor.help.isVisible());
+		}
+		else
+		{
+			editor.showHelp();
+		}
+	});
+	
+	this.addAction('toggleOutline', function(editor)
+	{
+		if (editor.outline == null)
+		{
+			editor.showOutline();
+		}
+		else
+		{
+			editor.outline.setVisible(!editor.outline.isVisible());
+		}
+	});
+	
+	this.addAction('toggleConsole', function(editor)
+	{
+		mxLog.setVisible(!mxLog.isVisible());
+	});
+};
+
+/**
+ * Function: configure
+ *
+ * Configures the editor using the specified node. To load the
+ * configuration from a given URL the following code can be used to obtain
+ * the XML node.
+ * 
+ * (code)
+ * var node = mxUtils.load(url).getDocumentElement();
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * node - XML node that contains the configuration.
+ */
+mxEditor.prototype.configure = function (node)
+{
+	if (node != null)
+	{
+		// Creates a decoder for the XML data
+		// and uses it to configure the editor
+		var dec = new mxCodec(node.ownerDocument);
+		dec.decode(node, this);
+		
+		// Resets the counters, modified state and
+		// command history
+		this.resetHistory();
+	}
+};
+
+/**
+ * Function: resetFirstTime
+ * 
+ * Resets the cookie that is used to remember if the editor has already
+ * been used.
+ */
+mxEditor.prototype.resetFirstTime = function ()
+{
+	document.cookie =
+		'mxgraph=seen; expires=Fri, 27 Jul 2001 02:47:11 UTC; path=/';
+};
+
+/**
+ * Function: resetHistory
+ * 
+ * Resets the command history, modified state and counters.
+ */
+mxEditor.prototype.resetHistory = function ()
+{
+	this.lastSnapshot = new Date().getTime();
+	this.undoManager.clear();
+	this.ignoredChanges = 0;
+	this.setModified(false);
+};
+
+/**
+ * Function: addAction
+ * 
+ * Binds the specified actionname to the specified function.
+ * 
+ * Parameters:
+ * 
+ * actionname - String that specifies the name of the action
+ * to be added.
+ * funct - Function that implements the new action. The first
+ * argument of the function is the editor it is used
+ * with, the second argument is the cell it operates
+ * upon.
+ * 
+ * Example:
+ * (code)
+ * editor.addAction('test', function(editor, cell)
+ * {
+ * 		mxUtils.alert("test "+cell);
+ * });
+ * (end)
+ */
+mxEditor.prototype.addAction = function (actionname, funct)
+{
+	this.actions[actionname] = funct;
+};
+
+/**
+ * Function: execute
+ * 
+ * Executes the function with the given name in <actions> passing the
+ * editor instance and given cell as the first and second argument. All
+ * additional arguments are passed to the action as well. This method
+ * contains a try-catch block and displays an error message if an action
+ * causes an exception. The exception is re-thrown after the error
+ * message was displayed.
+ * 
+ * Example:
+ * 
+ * (code)
+ * editor.execute("showProperties", cell);
+ * (end)
+ */
+mxEditor.prototype.execute = function (actionname, cell, evt)
+{
+	var action = this.actions[actionname];
+	
+	if (action != null)
+	{
+		try
+		{
+			// Creates the array of arguments by replacing the actionname
+			// with the editor instance in the args of this function
+			var args = arguments;
+			args[0] = this;
+			
+			// Invokes the function on the editor using the args
+			action.apply(this, args);
+		}
+		catch (e)
+		{
+			mxUtils.error('Cannot execute ' + actionname +
+				': ' + e.message, 280, true);
+			
+			throw e;
+		}
+	}
+	else
+	{
+		mxUtils.error('Cannot find action '+actionname, 280, true);
+	}
+};
+
+/**
+ * Function: addTemplate
+ * 
+ * Adds the specified template under the given name in <templates>.
+ */
+mxEditor.prototype.addTemplate = function (name, template)
+{
+	this.templates[name] = template;
+};
+
+/**
+ * Function: getTemplate
+ * 
+ * Returns the template for the given name.
+ */
+mxEditor.prototype.getTemplate = function (name)
+{
+	return this.templates[name];
+};
+
+/**
+ * Function: createGraph
+ * 
+ * Creates the <graph> for the editor. The graph is created with no
+ * container and is initialized from <setGraphContainer>.
+ */
+mxEditor.prototype.createGraph = function ()
+{
+	var graph = new mxGraph(null, null, this.graphRenderHint);
+	
+	// Enables rubberband, tooltips, panning
+	graph.setTooltips(true);
+	graph.setPanning(true);
+
+	// Overrides the dblclick method on the graph to
+	// invoke the dblClickAction for a cell and reset
+	// the selection tool in the toolbar
+	this.installDblClickHandler(graph);
+	
+	// Installs the command history
+	this.installUndoHandler(graph);
+
+	// Installs the handlers for the root event
+	this.installDrillHandler(graph);
+	
+	// Installs the handler for validation
+	this.installChangeHandler(graph);
+
+	// Installs the handler for calling the
+	// insert function and consume the
+	// event if an insert function is defined
+	this.installInsertHandler(graph);
+
+	// Redirects the function for creating the
+	// popupmenu items
+	graph.popupMenuHandler.factoryMethod =
+		mxUtils.bind(this, function(menu, cell, evt)
+		{
+			return this.createPopupMenu(menu, cell, evt);
+		});
+
+	// Redirects the function for creating
+	// new connections in the diagram
+	graph.connectionHandler.factoryMethod =
+		mxUtils.bind(this, function(source, target)
+		{
+			return this.createEdge(source, target);
+		});
+	
+	// Maintains swimlanes and installs autolayout
+	this.createSwimlaneManager(graph);
+	this.createLayoutManager(graph);
+	
+	return graph;
+};
+
+/**
+ * Function: createSwimlaneManager
+ * 
+ * Sets the graph's container using <mxGraph.init>.
+ */
+mxEditor.prototype.createSwimlaneManager = function (graph)
+{
+	var swimlaneMgr = new mxSwimlaneManager(graph, false);
+
+	swimlaneMgr.isHorizontal = mxUtils.bind(this, function()
+	{
+		return this.horizontalFlow;
+	});
+	
+	swimlaneMgr.isEnabled = mxUtils.bind(this, function()
+	{
+		return this.maintainSwimlanes;
+	});
+	
+	return swimlaneMgr;
+};
+
+/**
+ * Function: createLayoutManager
+ * 
+ * Creates a layout manager for the swimlane and diagram layouts, that
+ * is, the locally defined inter- and intraswimlane layouts.
+ */
+mxEditor.prototype.createLayoutManager = function (graph)
+{
+	var layoutMgr = new mxLayoutManager(graph);
+	
+	var self = this; // closure
+	layoutMgr.getLayout = function(cell)
+	{
+		var layout = null;
+		var model = self.graph.getModel();
+		
+		if (model.getParent(cell) != null)
+		{
+			// Executes the swimlane layout if a child of
+			// a swimlane has been changed. The layout is
+			// lazy created in createSwimlaneLayout.
+			if (self.layoutSwimlanes &&
+				graph.isSwimlane(cell))
+			{
+				if (self.swimlaneLayout == null)
+				{
+					self.swimlaneLayout = self.createSwimlaneLayout();
+				}
+				
+				layout = self.swimlaneLayout;
+			}
+			
+			// Executes the diagram layout if the modified
+			// cell is a top-level cell. The layout is
+			// lazy created in createDiagramLayout.
+			else if (self.layoutDiagram &&
+				(graph.isValidRoot(cell) ||
+				model.getParent(model.getParent(cell)) == null))
+			{
+				if (self.diagramLayout == null)
+				{
+					self.diagramLayout = self.createDiagramLayout();
+				}
+				
+				layout = self.diagramLayout;
+			}
+		}
+			
+		return layout;
+	};
+	
+	return layoutMgr;
+};
+
+/**
+ * Function: setGraphContainer
+ * 
+ * Sets the graph's container using <mxGraph.init>.
+ */
+mxEditor.prototype.setGraphContainer = function (container)
+{
+	if (this.graph.container == null)
+	{
+		// Creates the graph instance inside the given container and render hint
+		//this.graph = new mxGraph(container, null, this.graphRenderHint);
+		this.graph.init(container);
+
+		// Install rubberband selection as the last
+		// action handler in the chain
+		this.rubberband = new mxRubberband(this.graph);
+
+		// Disables the context menu
+		if (this.disableContextMenu)
+		{
+			mxEvent.disableContextMenu(container);
+		}
+
+		// Workaround for stylesheet directives in IE
+		if (mxClient.IS_QUIRKS)
+		{
+			new mxDivResizer(container);
+		}
+	}
+};
+
+/**
+ * Function: installDblClickHandler
+ * 
+ * Overrides <mxGraph.dblClick> to invoke <dblClickAction>
+ * on a cell and reset the selection tool in the toolbar.
+ */
+mxEditor.prototype.installDblClickHandler = function (graph)
+{
+	// Installs a listener for double click events
+	graph.addListener(mxEvent.DOUBLE_CLICK,
+		mxUtils.bind(this, function(sender, evt)
+		{
+			var cell = evt.getProperty('cell');
+			
+			if (cell != null &&
+				graph.isEnabled() &&
+				this.dblClickAction != null)
+			{
+				this.execute(this.dblClickAction, cell);
+				evt.consume();
+			}
+		})
+	);
+};
+		
+/**
+ * Function: installUndoHandler
+ * 
+ * Adds the <undoManager> to the graph model and the view.
+ */
+mxEditor.prototype.installUndoHandler = function (graph)
+{				
+	var listener = mxUtils.bind(this, function(sender, evt)
+	{
+		var edit = evt.getProperty('edit');
+		this.undoManager.undoableEditHappened(edit);
+	});
+	
+	graph.getModel().addListener(mxEvent.UNDO, listener);
+	graph.getView().addListener(mxEvent.UNDO, listener);
+
+	// Keeps the selection state in sync
+	var undoHandler = function(sender, evt)
+	{
+		var changes = evt.getProperty('edit').changes;
+		graph.setSelectionCells(graph.getSelectionCellsForChanges(changes));
+	};
+	
+	this.undoManager.addListener(mxEvent.UNDO, undoHandler);
+	this.undoManager.addListener(mxEvent.REDO, undoHandler);
+};
+		
+/**
+ * Function: installDrillHandler
+ * 
+ * Installs listeners for dispatching the <root> event.
+ */
+mxEditor.prototype.installDrillHandler = function (graph)
+{				
+	var listener = mxUtils.bind(this, function(sender)
+	{
+		this.fireEvent(new mxEventObject(mxEvent.ROOT));
+	});
+	
+	graph.getView().addListener(mxEvent.DOWN, listener);
+	graph.getView().addListener(mxEvent.UP, listener);
+};
+
+/**
+ * Function: installChangeHandler
+ * 
+ * Installs the listeners required to automatically validate
+ * the graph. On each change of the root, this implementation
+ * fires a <root> event.
+ */
+mxEditor.prototype.installChangeHandler = function (graph)
+{
+	var listener = mxUtils.bind(this, function(sender, evt)
+	{
+		// Updates the modified state
+		this.setModified(true);
+
+		// Automatically validates the graph
+		// after each change
+		if (this.validating == true)
+		{
+			graph.validateGraph();
+		}
+
+		// Checks if the root has been changed
+		var changes = evt.getProperty('edit').changes;
+		
+		for (var i = 0; i < changes.length; i++)
+		{
+			var change = changes[i];
+			
+			if (change instanceof mxRootChange ||
+				(change instanceof mxValueChange &&
+				change.cell == this.graph.model.root) ||
+				(change instanceof mxCellAttributeChange &&
+				change.cell == this.graph.model.root))
+			{
+				this.fireEvent(new mxEventObject(mxEvent.ROOT));
+				break;
+			}
+		}
+	});
+	
+	graph.getModel().addListener(mxEvent.CHANGE, listener);
+};
+
+/**
+ * Function: installInsertHandler
+ * 
+ * Installs the handler for invoking <insertFunction> if
+ * one is defined.
+ */
+mxEditor.prototype.installInsertHandler = function (graph)
+{
+	var self = this; // closure
+	var insertHandler =
+	{
+		mouseDown: function(sender, me)
+		{
+			if (self.insertFunction != null &&
+				!me.isPopupTrigger() &&
+				(self.forcedInserting ||
+				me.getState() == null))
+			{
+				self.graph.clearSelection();
+				self.insertFunction(me.getEvent(), me.getCell());
+
+				// Consumes the rest of the events
+				// for this gesture (down, move, up)
+				this.isActive = true;
+				me.consume();
+			}
+		},
+		
+		mouseMove: function(sender, me)
+		{
+			if (this.isActive)
+			{
+				me.consume();
+			}
+		},
+		
+		mouseUp: function(sender, me)
+		{
+			if (this.isActive)
+			{
+				this.isActive = false;
+				me.consume();
+			}
+		}
+	};
+	
+	graph.addMouseListener(insertHandler);
+};
+
+/**
+ * Function: createDiagramLayout
+ * 
+ * Creates the layout instance used to layout the
+ * swimlanes in the diagram.
+ */
+mxEditor.prototype.createDiagramLayout = function ()
+{
+	var gs = this.graph.gridSize;
+	var layout = new mxStackLayout(this.graph, !this.horizontalFlow,
+		 this.swimlaneSpacing, 2*gs, 2*gs);
+	
+	// Overrides isIgnored to only take into account swimlanes
+	layout.isVertexIgnored = function(cell)
+	{
+		return !layout.graph.isSwimlane(cell);
+	};
+	
+	return layout;
+};
+
+/**
+ * Function: createSwimlaneLayout
+ * 
+ * Creates the layout instance used to layout the
+ * children of each swimlane.
+ */
+mxEditor.prototype.createSwimlaneLayout = function ()
+{
+	return new mxCompactTreeLayout(this.graph, this.horizontalFlow);
+};
+
+/**
+ * Function: createToolbar
+ * 
+ * Creates the <toolbar> with no container.
+ */
+mxEditor.prototype.createToolbar = function ()
+{
+	return new mxDefaultToolbar(null, this);
+};
+
+/**
+ * Function: setToolbarContainer
+ * 
+ * Initializes the toolbar for the given container.
+ */
+mxEditor.prototype.setToolbarContainer = function (container)
+{
+	this.toolbar.init(container);
+	
+	// Workaround for stylesheet directives in IE
+	if (mxClient.IS_QUIRKS)
+	{
+		new mxDivResizer(container);
+	}
+};
+
+/**
+ * Function: setStatusContainer
+ * 
+ * Creates the <status> using the specified container.
+ * 
+ * This implementation adds listeners in the editor to 
+ * display the last saved time and the current filename 
+ * in the status bar.
+ * 
+ * Parameters:
+ * 
+ * container - DOM node that will contain the statusbar.
+ */
+mxEditor.prototype.setStatusContainer = function (container)
+{
+	if (this.status == null)
+	{
+		this.status = container;
+		
+		// Prints the last saved time in the status bar
+		// when files are saved
+		this.addListener(mxEvent.SAVE, mxUtils.bind(this, function()
+		{
+			var tstamp = new Date().toLocaleString();
+			this.setStatus((mxResources.get(this.lastSavedResource) ||
+				this.lastSavedResource)+': '+tstamp);
+		}));
+		
+		// Updates the statusbar to display the filename
+		// when new files are opened
+		this.addListener(mxEvent.OPEN, mxUtils.bind(this, function()
+		{
+			this.setStatus((mxResources.get(this.currentFileResource) ||
+				this.currentFileResource)+': '+this.filename);
+		}));
+		
+		// Workaround for stylesheet directives in IE
+		if (mxClient.IS_QUIRKS)
+		{
+			new mxDivResizer(container);
+		}
+	}
+};
+
+/**
+ * Function: setStatus
+ * 
+ * Display the specified message in the status bar.
+ * 
+ * Parameters:
+ * 
+ * message - String the specified the message to
+ * be displayed.
+ */
+mxEditor.prototype.setStatus = function (message)
+{
+	if (this.status != null && message != null)
+	{
+		this.status.innerHTML = message;
+	}
+};
+
+/**
+ * Function: setTitleContainer
+ * 
+ * Creates a listener to update the inner HTML of the
+ * specified DOM node with the value of <getTitle>.
+ * 
+ * Parameters:
+ * 
+ * container - DOM node that will contain the title.
+ */
+mxEditor.prototype.setTitleContainer = function (container)
+{
+	this.addListener(mxEvent.ROOT, mxUtils.bind(this, function(sender)
+	{
+		container.innerHTML = this.getTitle();
+	}));
+
+	// Workaround for stylesheet directives in IE
+	if (mxClient.IS_QUIRKS)
+	{
+		new mxDivResizer(container);
+	}
+};
+
+/**
+ * Function: treeLayout
+ * 
+ * Executes a vertical or horizontal compact tree layout
+ * using the specified cell as an argument. The cell may
+ * either be a group or the root of a tree.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to use in the compact tree layout.
+ * horizontal - Optional boolean to specify the tree's
+ * orientation. Default is true.
+ */
+mxEditor.prototype.treeLayout = function (cell, horizontal)
+{
+	if (cell != null)
+	{
+		var layout = new mxCompactTreeLayout(this.graph, horizontal);
+		layout.execute(cell);
+	}
+};
+
+/**
+ * Function: getTitle
+ * 
+ * Returns the string value for the current root of the
+ * diagram.
+ */
+mxEditor.prototype.getTitle = function ()
+{
+	var title = '';
+	var graph = this.graph;
+	var cell = graph.getCurrentRoot();
+	
+	while (cell != null &&
+		   graph.getModel().getParent(
+				graph.getModel().getParent(cell)) != null)
+	{
+		// Append each label of a valid root
+		if (graph.isValidRoot(cell))
+		{
+			title = ' > ' +
+			graph.convertValueToString(cell) + title;
+		}
+		
+		cell = graph.getModel().getParent(cell);
+	}
+	
+	var prefix = this.getRootTitle();
+	
+	return prefix + title;
+};
+
+/**
+ * Function: getRootTitle
+ * 
+ * Returns the string value of the root cell in
+ * <mxGraph.model>.
+ */
+mxEditor.prototype.getRootTitle = function ()
+{
+	var root = this.graph.getModel().getRoot();
+	return this.graph.convertValueToString(root);
+};
+
+/**
+ * Function: undo
+ * 
+ * Undo the last change in <graph>.
+ */
+mxEditor.prototype.undo = function ()
+{
+	this.undoManager.undo();
+};
+
+/**
+ * Function: redo
+ * 
+ * Redo the last change in <graph>.
+ */
+mxEditor.prototype.redo = function ()
+{
+	this.undoManager.redo();
+};
+
+/**
+ * Function: groupCells
+ * 
+ * Invokes <createGroup> to create a new group cell and the invokes
+ * <mxGraph.groupCells>, using the grid size of the graph as the spacing
+ * in the group's content area.
+ */
+mxEditor.prototype.groupCells = function ()
+{
+	var border = (this.groupBorderSize != null) ?
+		this.groupBorderSize :
+		this.graph.gridSize;
+	return this.graph.groupCells(this.createGroup(), border);
+};
+
+/**
+ * Function: createGroup
+ * 
+ * Creates and returns a clone of <defaultGroup> to be used
+ * as a new group cell in <group>.
+ */
+mxEditor.prototype.createGroup = function ()
+{
+	var model = this.graph.getModel();
+	
+	return model.cloneCell(this.defaultGroup);
+};
+
+/**
+ * Function: open
+ * 
+ * Opens the specified file synchronously and parses it using
+ * <readGraphModel>. It updates <filename> and fires an <open>-event after
+ * the file has been opened. Exceptions should be handled as follows:
+ * 
+ * (code)
+ * try
+ * {
+ *   editor.open(filename);
+ * }
+ * catch (e)
+ * {
+ *   mxUtils.error('Cannot open ' + filename +
+ *     ': ' + e.message, 280, true);
+ * }
+ * (end)
+ *
+ * Parameters:
+ * 
+ * filename - URL of the file to be opened.
+ */
+mxEditor.prototype.open = function (filename)
+{
+	if (filename != null)
+	{
+		var xml = mxUtils.load(filename).getXml();
+		this.readGraphModel(xml.documentElement);
+		this.filename = filename;
+		
+		this.fireEvent(new mxEventObject(mxEvent.OPEN, 'filename', filename));
+	}
+};
+
+/**
+ * Function: readGraphModel
+ * 
+ * Reads the specified XML node into the existing graph model and resets
+ * the command history and modified state.
+ */
+mxEditor.prototype.readGraphModel = function (node)
+{
+	var dec = new mxCodec(node.ownerDocument);
+	dec.decode(node, this.graph.getModel());
+	this.resetHistory();
+};
+
+/**
+ * Function: save
+ * 
+ * Posts the string returned by <writeGraphModel> to the given URL or the
+ * URL returned by <getUrlPost>. The actual posting is carried out by
+ * <postDiagram>. If the URL is null then the resulting XML will be
+ * displayed using <mxUtils.popup>. Exceptions should be handled as
+ * follows:
+ * 
+ * (code)
+ * try
+ * {
+ *   editor.save();
+ * }
+ * catch (e)
+ * {
+ *   mxUtils.error('Cannot save : ' + e.message, 280, true);
+ * }
+ * (end)
+ */
+mxEditor.prototype.save = function (url, linefeed)
+{
+	// Gets the URL to post the data to
+	url = url || this.getUrlPost();
+
+	// Posts the data if the URL is not empty
+	if (url != null && url.length > 0)
+	{
+		var data = this.writeGraphModel(linefeed);
+		this.postDiagram(url, data);
+		
+		// Resets the modified flag
+		this.setModified(false);
+	}
+	
+	// Dispatches a save event
+	this.fireEvent(new mxEventObject(mxEvent.SAVE, 'url', url));
+};
+
+/**
+ * Function: postDiagram
+ * 
+ * Hook for subclassers to override the posting of a diagram
+ * represented by the given node to the given URL. This fires
+ * an asynchronous <post> event if the diagram has been posted.
+ * 
+ * Example:
+ * 
+ * To replace the diagram with the diagram in the response, use the
+ * following code.
+ * 
+ * (code)
+ * editor.addListener(mxEvent.POST, function(sender, evt)
+ * {
+ *   // Process response (replace diagram)
+ *   var req = evt.getProperty('request');
+ *   var root = req.getDocumentElement();
+ *   editor.graph.readGraphModel(root)
+ * });
+ * (end)
+ */
+mxEditor.prototype.postDiagram = function (url, data)
+{
+	if (this.escapePostData)
+	{
+		data = encodeURIComponent(data);
+	}
+
+	mxUtils.post(url, this.postParameterName+'='+data,
+		mxUtils.bind(this, function(req)
+		{
+			this.fireEvent(new mxEventObject(mxEvent.POST,
+				'request', req, 'url', url, 'data', data));
+		})
+	);
+};
+
+/**
+ * Function: writeGraphModel
+ * 
+ * Hook to create the string representation of the diagram. The default
+ * implementation uses an <mxCodec> to encode the graph model as
+ * follows:
+ * 
+ * (code)
+ * var enc = new mxCodec();
+ * var node = enc.encode(this.graph.getModel());
+ * return mxUtils.getXml(node, this.linefeed);
+ * (end)
+ * 
+ * Parameters:
+ * 
+ * linefeed - Optional character to be used as the linefeed. Default is
+ * <linefeed>.
+ */
+mxEditor.prototype.writeGraphModel = function (linefeed)
+{
+	linefeed = (linefeed != null) ? linefeed : this.linefeed;
+	var enc = new mxCodec();
+	var node = enc.encode(this.graph.getModel());
+
+	return mxUtils.getXml(node, linefeed);
+};
+
+/**
+ * Function: getUrlPost
+ * 
+ * Returns the URL to post the diagram to. This is used
+ * in <save>. The default implementation returns <urlPost>,
+ * adding <code>?draft=true</code>.
+ */
+mxEditor.prototype.getUrlPost = function ()
+{
+	return this.urlPost;
+};
+
+/**
+ * Function: getUrlImage
+ * 
+ * Returns the URL to create the image with. This is typically
+ * the URL of a backend which accepts an XML representation
+ * of a graph view to create an image. The function is used
+ * in the image action to create an image. This implementation
+ * returns <urlImage>.
+ */
+mxEditor.prototype.getUrlImage = function ()
+{
+	return this.urlImage;
+};
+
+/**
+ * Function: swapStyles
+ * 
+ * Swaps the styles for the given names in the graph's
+ * stylesheet and refreshes the graph.
+ */
+mxEditor.prototype.swapStyles = function (first, second)
+{
+	var style = this.graph.getStylesheet().styles[second];
+	this.graph.getView().getStylesheet().putCellStyle(
+		second, this.graph.getStylesheet().styles[first]);
+	this.graph.getStylesheet().putCellStyle(first, style);
+	this.graph.refresh();
+};
+
+/**
+ * Function: showProperties
+ * 
+ * Creates and shows the properties dialog for the given
+ * cell. The content area of the dialog is created using
+ * <createProperties>.
+ */
+mxEditor.prototype.showProperties = function (cell)
+{
+	cell = cell || this.graph.getSelectionCell();
+	
+	// Uses the root node for the properties dialog
+	// if not cell was passed in and no cell is
+	// selected
+	if (cell == null)
+	{
+		cell = this.graph.getCurrentRoot();
+		
+		if (cell == null)
+		{
+			cell = this.graph.getModel().getRoot();
+		}
+	}
+	
+	if (cell != null)
+	{
+		// Makes sure there is no in-place editor in the
+		// graph and computes the location of the dialog
+		this.graph.stopEditing(true);
+
+		var offset = mxUtils.getOffset(this.graph.container);
+		var x = offset.x+10;
+		var y = offset.y;
+		
+		// Avoids moving the dialog if it is alredy open
+		if (this.properties != null && !this.movePropertiesDialog)
+		{
+			x = this.properties.getX();
+			y = this.properties.getY();
+		}
+		
+		// Places the dialog near the cell for which it
+		// displays the properties
+		else
+		{
+			var bounds = this.graph.getCellBounds(cell);
+			
+			if (bounds != null)
+			{
+				x += bounds.x+Math.min(200, bounds.width);
+				y += bounds.y;				
+			}			
+		}
+		
+		// Hides the existing properties dialog and creates a new one with the
+		// contents created in the hook method
+		this.hideProperties();
+		var node = this.createProperties(cell);
+		
+		if (node != null)
+		{
+			// Displays the contents in a window and stores a reference to the
+			// window for later hiding of the window
+			this.properties = new mxWindow(mxResources.get(this.propertiesResource) ||
+				this.propertiesResource, node, x, y, this.propertiesWidth, this.propertiesHeight, false);
+			this.properties.setVisible(true);
+		}
+	}
+};
+
+/**
+ * Function: isPropertiesVisible
+ * 
+ * Returns true if the properties dialog is currently visible.
+ */
+mxEditor.prototype.isPropertiesVisible = function ()
+{
+	return this.properties != null;
+};
+
+/**
+ * Function: createProperties
+ * 
+ * Creates and returns the DOM node that represents the contents
+ * of the properties dialog for the given cell. This implementation
+ * works for user objects that are XML nodes and display all the
+ * node attributes in a form.
+ */
+mxEditor.prototype.createProperties = function (cell)
+{
+	var model = this.graph.getModel();
+	var value = model.getValue(cell);
+	
+	if (mxUtils.isNode(value))
+	{
+		// Creates a form for the user object inside
+		// the cell
+		var form = new mxForm('properties');
+		
+		// Adds a readonly field for the cell id
+		var id = form.addText('ID', cell.getId());
+		id.setAttribute('readonly', 'true');
+
+		var geo = null;
+		var yField = null;
+		var xField = null;
+		var widthField = null;
+		var heightField = null;
+
+		// Adds fields for the location and size
+		if (model.isVertex(cell))
+		{
+			geo = model.getGeometry(cell);
+			
+			if (geo != null)
+			{
+				yField = form.addText('top', geo.y);
+				xField = form.addText('left', geo.x);
+				widthField = form.addText('width', geo.width);
+				heightField = form.addText('height', geo.height);
+			}
+		}
+		
+		// Adds a field for the cell style			
+		var tmp = model.getStyle(cell);
+		var style = form.addText('Style', tmp || '');
+		
+		// Creates textareas for each attribute of the
+		// user object within the cell
+		var attrs = value.attributes;
+		var texts = [];
+		
+		for (var i = 0; i < attrs.length; i++)
+		{
+			// Creates a textarea with more lines for
+			// the cell label
+			var val = attrs[i].value;
+			texts[i] = form.addTextarea(attrs[i].nodeName, val,
+				(attrs[i].nodeName == 'label') ? 4 : 2);
+		}
+		
+		// Adds an OK and Cancel button to the dialog
+		// contents and implements the respective
+		// actions below
+		
+		// Defines the function to be executed when the
+		// OK button is pressed in the dialog
+		var okFunction = mxUtils.bind(this, function()
+		{
+			// Hides the dialog
+			this.hideProperties();
+			
+			// Supports undo for the changes on the underlying
+			// XML structure / XML node attribute changes.
+			model.beginUpdate();
+			try
+			{
+				if (geo != null)
+				{
+					geo = geo.clone();
+					
+					geo.x = parseFloat(xField.value);
+					geo.y = parseFloat(yField.value);
+					geo.width = parseFloat(widthField.value);
+					geo.height = parseFloat(heightField.value);
+					
+					model.setGeometry(cell, geo);
+				}
+				
+				// Applies the style
+				if (style.value.length > 0)
+				{
+					model.setStyle(cell, style.value);
+				}
+				else
+				{
+					model.setStyle(cell, null);
+				}
+				
+				// Creates an undoable change for each
+				// attribute and executes it using the
+				// model, which will also make the change
+				// part of the current transaction
+				for (var i=0; i<attrs.length; i++)
+				{
+					var edit = new mxCellAttributeChange(
+						cell, attrs[i].nodeName,
+						texts[i].value);
+					model.execute(edit);
+				}
+				
+				// Checks if the graph wants cells to 
+				// be automatically sized and updates
+				// the size as an undoable step if
+				// the feature is enabled
+				if (this.graph.isAutoSizeCell(cell))
+				{
+					this.graph.updateCellSize(cell);
+				}
+			}
+			finally
+			{
+				model.endUpdate();
+			}
+		});
+		
+		// Defines the function to be executed when the
+		// Cancel button is pressed in the dialog
+		var cancelFunction = mxUtils.bind(this, function()
+		{
+			// Hides the dialog
+			this.hideProperties();
+		});
+		
+		form.addButtons(okFunction, cancelFunction);
+		
+		return form.table;
+	}
+
+	return null;
+};
+
+/**
+ * Function: hideProperties
+ * 
+ * Hides the properties dialog.
+ */
+mxEditor.prototype.hideProperties = function ()
+{
+	if (this.properties != null)
+	{
+		this.properties.destroy();
+		this.properties = null;
+	}
+};
+
+/**
+ * Function: showTasks
+ * 
+ * Shows the tasks window. The tasks window is created using <createTasks>. The
+ * default width of the window is 200 pixels, the y-coordinate of the location
+ * can be specifies in <tasksTop> and the x-coordinate is right aligned with a
+ * 20 pixel offset from the right border. To change the location of the tasks
+ * window, the following code can be used:
+ * 
+ * (code)
+ * var oldShowTasks = mxEditor.prototype.showTasks;
+ * mxEditor.prototype.showTasks = function()
+ * {
+ *   oldShowTasks.apply(this, arguments); // "supercall"
+ *   
+ *   if (this.tasks != null)
+ *   {
+ *     this.tasks.setLocation(10, 10);
+ *   }
+ * };
+ * (end)
+ */
+mxEditor.prototype.showTasks = function ()
+{
+	if (this.tasks == null)
+	{
+		var div = document.createElement('div');
+		div.style.padding = '4px';
+		div.style.paddingLeft = '20px';
+		var w = document.body.clientWidth;
+		var wnd = new mxWindow(
+			mxResources.get(this.tasksResource) ||
+			this.tasksResource,
+			div, w - 220, this.tasksTop, 200);
+		wnd.setClosable(true);
+		wnd.destroyOnClose = false;
+		
+		// Installs a function to update the contents
+		// of the tasks window on every change of the
+		// model, selection or root.
+		var funct = mxUtils.bind(this, function(sender)
+		{
+			mxEvent.release(div);
+			div.innerHTML = '';
+			this.createTasks(div);
+		});
+		
+		this.graph.getModel().addListener(mxEvent.CHANGE, funct);
+		this.graph.getSelectionModel().addListener(mxEvent.CHANGE, funct);
+		this.graph.addListener(mxEvent.ROOT, funct);
+		
+		// Assigns the icon to the tasks window
+		if (this.tasksWindowImage != null)
+		{
+			wnd.setImage(this.tasksWindowImage);
+		}
+		
+		this.tasks = wnd;
+		this.createTasks(div);
+	}
+	
+	this.tasks.setVisible(true);
+};
+		
+/**
+ * Function: refreshTasks
+ * 
+ * Updates the contents of the tasks window using <createTasks>.
+ */
+mxEditor.prototype.refreshTasks = function (div)
+{
+	if (this.tasks != null)
+	{
+		var div = this.tasks.content;
+		mxEvent.release(div);
+		div.innerHTML = '';
+		this.createTasks(div);
+	}
+};
+		
+/**
+ * Function: createTasks
+ * 
+ * Updates the contents of the given DOM node to
+ * display the tasks associated with the current
+ * editor state. This is invoked whenever there
+ * is a possible change of state in the editor.
+ * Default implementation is empty.
+ */
+mxEditor.prototype.createTasks = function (div)
+{
+	// override
+};
+	
+/**
+ * Function: showHelp
+ * 
+ * Shows the help window. If the help window does not exist
+ * then it is created using an iframe pointing to the resource
+ * for the <code>urlHelp</code> key or <urlHelp> if the resource
+ * is undefined.
+ */
+mxEditor.prototype.showHelp = function (tasks)
+{
+	if (this.help == null)
+	{
+		var frame = document.createElement('iframe');
+		frame.setAttribute('src', mxResources.get('urlHelp') || this.urlHelp);
+		frame.setAttribute('height', '100%');
+		frame.setAttribute('width', '100%');
+		frame.setAttribute('frameBorder', '0');
+		frame.style.backgroundColor = 'white';
+	
+		var w = document.body.clientWidth;
+		var h = (document.body.clientHeight || document.documentElement.clientHeight);
+		
+		var wnd = new mxWindow(mxResources.get(this.helpResource) || this.helpResource,
+			frame, (w-this.helpWidth)/2, (h-this.helpHeight)/3, this.helpWidth, this.helpHeight);
+		wnd.setMaximizable(true);
+		wnd.setClosable(true);
+		wnd.destroyOnClose = false;
+		wnd.setResizable(true);
+
+		// Assigns the icon to the help window
+		if (this.helpWindowImage != null)
+		{
+			wnd.setImage(this.helpWindowImage);
+		}
+		
+		// Workaround for ignored iframe height 100% in FF
+		if (mxClient.IS_NS)
+		{
+			var handler = function(sender)
+			{
+				var h = wnd.div.offsetHeight;
+				frame.setAttribute('height', (h-26)+'px');
+			};
+			
+			wnd.addListener(mxEvent.RESIZE_END, handler);
+			wnd.addListener(mxEvent.MAXIMIZE, handler);
+			wnd.addListener(mxEvent.NORMALIZE, handler);
+			wnd.addListener(mxEvent.SHOW, handler);
+		}
+		
+		this.help = wnd;
+	}
+	
+	this.help.setVisible(true);
+};
+
+/**
+ * Function: showOutline
+ * 
+ * Shows the outline window. If the window does not exist, then it is
+ * created using an <mxOutline>.
+ */
+mxEditor.prototype.showOutline = function ()
+{
+	var create = this.outline == null;
+	
+	if (create)
+	{
+		var div = document.createElement('div');
+		
+		div.style.overflow = 'hidden';
+		div.style.position = 'relative';
+		div.style.width = '100%';
+		div.style.height = '100%';
+		div.style.background = 'white';
+		div.style.cursor = 'move';
+		
+		if (document.documentMode == 8)
+		{
+			div.style.filter = 'progid:DXImageTransform.Microsoft.alpha(opacity=100)';
+		}
+		
+		var wnd = new mxWindow(
+			mxResources.get(this.outlineResource) ||
+			this.outlineResource,
+			div, 600, 480, 200, 200, false);
+				
+		// Creates the outline in the specified div
+		// and links it to the existing graph
+		var outline = new mxOutline(this.graph, div);			
+		wnd.setClosable(true);
+		wnd.setResizable(true);
+		wnd.destroyOnClose = false;
+		
+		wnd.addListener(mxEvent.RESIZE_END, function()
+		{
+			outline.update();
+		});
+		
+		this.outline = wnd;
+		this.outline.outline = outline;
+	}
+	
+	// Finally shows the outline
+	this.outline.setVisible(true);
+	this.outline.outline.update(true);
+};
+		
+/**
+ * Function: setMode
+ *
+ * Puts the graph into the specified mode. The following modenames are
+ * supported:
+ * 
+ * select - Selects using the left mouse button, new connections
+ * are disabled.
+ * connect - Selects using the left mouse button or creates new
+ * connections if mouse over cell hotspot. See <mxConnectionHandler>.
+ * pan - Pans using the left mouse button, new connections are disabled.
+ */
+mxEditor.prototype.setMode = function(modename)
+{
+	if (modename == 'select')
+	{
+		this.graph.panningHandler.useLeftButtonForPanning = false;
+		this.graph.setConnectable(false);
+	}
+	else if (modename == 'connect')
+	{
+		this.graph.panningHandler.useLeftButtonForPanning = false;
+		this.graph.setConnectable(true);
+	}
+	else if (modename == 'pan')
+	{
+		this.graph.panningHandler.useLeftButtonForPanning = true;
+		this.graph.setConnectable(false);
+	}
+};
+
+/**
+ * Function: createPopupMenu
+ * 
+ * Uses <popupHandler> to create the menu in the graph's
+ * panning handler. The redirection is setup in
+ * <setToolbarContainer>.
+ */
+mxEditor.prototype.createPopupMenu = function (menu, cell, evt)
+{
+	this.popupHandler.createMenu(this, menu, cell, evt);
+};
+
+/**
+ * Function: createEdge
+ * 
+ * Uses <defaultEdge> as the prototype for creating new edges
+ * in the connection handler of the graph. The style of the
+ * edge will be overridden with the value returned by
+ * <getEdgeStyle>.
+ */
+mxEditor.prototype.createEdge = function (source, target)
+{
+	// Clones the defaultedge prototype
+	var e = null;
+	
+	if (this.defaultEdge != null)
+	{
+		var model = this.graph.getModel();
+		e = model.cloneCell(this.defaultEdge);
+	}
+	else
+	{
+		e = new mxCell('');
+		e.setEdge(true);
+		
+		var geo = new mxGeometry();
+		geo.relative = true;
+		e.setGeometry(geo);
+	}
+	
+	// Overrides the edge style
+	var style = this.getEdgeStyle();
+	
+	if (style != null)
+	{
+		e.setStyle(style);
+	}
+	
+	return e;
+};
+
+/**
+ * Function: getEdgeStyle
+ * 
+ * Returns a string identifying the style of new edges.
+ * The function is used in <createEdge> when new edges
+ * are created in the graph.
+ */
+mxEditor.prototype.getEdgeStyle = function ()
+{
+	return this.defaultEdgeStyle;
+};
+
+/**
+ * Function: consumeCycleAttribute
+ * 
+ * Returns the next attribute in <cycleAttributeValues>
+ * or null, if not attribute should be used in the
+ * specified cell.
+ */
+mxEditor.prototype.consumeCycleAttribute = function (cell)
+{
+	return (this.cycleAttributeValues != null &&
+		this.cycleAttributeValues.length > 0 &&
+		this.graph.isSwimlane(cell)) ?
+		this.cycleAttributeValues[this.cycleAttributeIndex++ %
+			this.cycleAttributeValues.length] : null;
+};
+
+/**
+ * Function: cycleAttribute
+ * 
+ * Uses the returned value from <consumeCycleAttribute>
+ * as the value for the <cycleAttributeName> key in
+ * the given cell's style.
+ */
+mxEditor.prototype.cycleAttribute = function (cell)
+{
+	if (this.cycleAttributeName != null)
+	{
+		var value = this.consumeCycleAttribute(cell);
+		
+		if (value != null)
+		{
+			cell.setStyle(cell.getStyle()+';'+
+				this.cycleAttributeName+'='+value);
+		}
+	}
+};
+
+/**
+ * Function: addVertex
+ * 
+ * Adds the given vertex as a child of parent at the specified
+ * x and y coordinate and fires an <addVertex> event.
+ */
+mxEditor.prototype.addVertex = function (parent, vertex, x, y)
+{
+	var model = this.graph.getModel();
+	
+	while (parent != null && !this.graph.isValidDropTarget(parent))
+	{
+		parent = model.getParent(parent);
+	}
+	
+	parent = (parent != null) ? parent : this.graph.getSwimlaneAt(x, y);
+	var scale = this.graph.getView().scale;
+	
+	var geo = model.getGeometry(vertex);
+	var pgeo = model.getGeometry(parent);
+	
+	if (this.graph.isSwimlane(vertex) &&
+		!this.graph.swimlaneNesting)
+	{
+		parent = null;
+	}
+	else if (parent == null && this.swimlaneRequired)
+	{
+		return null;
+	}
+	else if (parent != null && pgeo != null)
+	{
+		// Keeps vertex inside parent
+		var state = this.graph.getView().getState(parent);
+		
+		if (state != null)
+		{			
+			x -= state.origin.x * scale;
+			y -= state.origin.y * scale;
+			
+			if (this.graph.isConstrainedMoving)
+			{
+				var width = geo.width;
+				var height = geo.height;				
+				var tmp = state.x+state.width;
+				
+				if (x+width > tmp)
+				{
+					x -= x+width - tmp;
+				}
+				
+				tmp = state.y+state.height;
+				
+				if (y+height > tmp)
+				{
+					y -= y+height - tmp;
+				}
+			}
+		}
+		else if (pgeo != null)
+		{
+			x -= pgeo.x*scale;
+			y -= pgeo.y*scale;
+		}
+	}
+	
+	geo = geo.clone();
+	geo.x = this.graph.snap(x / scale -
+		this.graph.getView().translate.x -
+		this.graph.gridSize/2);
+	geo.y = this.graph.snap(y / scale -
+		this.graph.getView().translate.y -
+		this.graph.gridSize/2);
+	vertex.setGeometry(geo);
+	
+	if (parent == null)
+	{
+		parent = this.graph.getDefaultParent();
+	}
+
+	this.cycleAttribute(vertex);
+	this.fireEvent(new mxEventObject(mxEvent.BEFORE_ADD_VERTEX,
+			'vertex', vertex, 'parent', parent));
+
+	model.beginUpdate();
+	try
+	{
+		vertex = this.graph.addCell(vertex, parent);
+		
+		if (vertex != null)
+		{
+			this.graph.constrainChild(vertex);
+			
+			this.fireEvent(new mxEventObject(mxEvent.ADD_VERTEX, 'vertex', vertex));
+		}
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+	
+	if (vertex != null)
+	{
+		this.graph.setSelectionCell(vertex);
+		this.graph.scrollCellToVisible(vertex);
+		this.fireEvent(new mxEventObject(mxEvent.AFTER_ADD_VERTEX, 'vertex', vertex));
+	}
+	
+	return vertex;
+};
+
+/**
+ * Function: destroy
+ * 
+ * Removes the editor and all its associated resources. This does not
+ * normally need to be called, it is called automatically when the window
+ * unloads.
+ */
+mxEditor.prototype.destroy = function ()
+{
+	if (!this.destroyed)
+	{
+		this.destroyed = true;
+
+		if (this.tasks != null)
+		{
+			this.tasks.destroy();
+		}
+		
+		if (this.outline != null)
+		{
+			this.outline.destroy();
+		}
+		
+		if (this.properties != null)
+		{
+			this.properties.destroy();
+		}
+		
+		if (this.keyHandler != null)
+		{
+			this.keyHandler.destroy();
+		}
+		
+		if (this.rubberband != null)
+		{
+			this.rubberband.destroy();
+		}
+		
+		if (this.toolbar != null)
+		{
+			this.toolbar.destroy();
+		}
+		
+		if (this.graph != null)
+		{
+			this.graph.destroy();
+		}
+	
+		this.status = null;
+		this.templates = null;
+	}
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/handler/mxCellHighlight.js b/airavata-kubernetes/web-console/src/assets/js/handler/mxCellHighlight.js
new file mode 100644
index 0000000..937386a
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/handler/mxCellHighlight.js
@@ -0,0 +1,314 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCellHighlight
+ * 
+ * A helper class to highlight cells. Here is an example for a given cell.
+ * 
+ * (code)
+ * var highlight = new mxCellHighlight(graph, '#ff0000', 2);
+ * highlight.highlight(graph.view.getState(cell)));
+ * (end)
+ * 
+ * Constructor: mxCellHighlight
+ * 
+ * Constructs a cell highlight.
+ */
+function mxCellHighlight(graph, highlightColor, strokeWidth, dashed)
+{
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.highlightColor = (highlightColor != null) ? highlightColor : mxConstants.DEFAULT_VALID_COLOR;
+		this.strokeWidth = (strokeWidth != null) ? strokeWidth : mxConstants.HIGHLIGHT_STROKEWIDTH;
+		this.dashed = (dashed != null) ? dashed : false;
+		this.opacity = mxConstants.HIGHLIGHT_OPACITY;
+
+		// Updates the marker if the graph changes
+		this.repaintHandler = mxUtils.bind(this, function()
+		{
+			// Updates reference to state
+			if (this.state != null)
+			{
+				var tmp = this.graph.view.getState(this.state.cell);
+				
+				if (tmp == null)
+				{
+					this.hide();
+				}
+				else
+				{
+					this.state = tmp;
+					this.repaint();
+				}
+			}
+		});
+
+		this.graph.getView().addListener(mxEvent.SCALE, this.repaintHandler);
+		this.graph.getView().addListener(mxEvent.TRANSLATE, this.repaintHandler);
+		this.graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, this.repaintHandler);
+		this.graph.getModel().addListener(mxEvent.CHANGE, this.repaintHandler);
+		
+		// Hides the marker if the current root changes
+		this.resetHandler = mxUtils.bind(this, function()
+		{
+			this.hide();
+		});
+
+		this.graph.getView().addListener(mxEvent.DOWN, this.resetHandler);
+		this.graph.getView().addListener(mxEvent.UP, this.resetHandler);
+	}
+};
+
+/**
+ * Variable: keepOnTop
+ * 
+ * Specifies if the highlights should appear on top of everything
+ * else in the overlay pane. Default is false.
+ */
+mxCellHighlight.prototype.keepOnTop = false;
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxCellHighlight.prototype.graph = true;
+
+/**
+ * Variable: state
+ * 
+ * Reference to the <mxCellState>.
+ */
+mxCellHighlight.prototype.state = null;
+
+/**
+ * Variable: spacing
+ * 
+ * Specifies the spacing between the highlight for vertices and the vertex.
+ * Default is 2.
+ */
+mxCellHighlight.prototype.spacing = 2;
+
+/**
+ * Variable: resetHandler
+ * 
+ * Holds the handler that automatically invokes reset if the highlight
+ * should be hidden.
+ */
+mxCellHighlight.prototype.resetHandler = null;
+
+/**
+ * Function: setHighlightColor
+ * 
+ * Sets the color of the rectangle used to highlight drop targets.
+ * 
+ * Parameters:
+ * 
+ * color - String that represents the new highlight color.
+ */
+mxCellHighlight.prototype.setHighlightColor = function(color)
+{
+	this.highlightColor = color;
+	
+	if (this.shape != null)
+	{
+		this.shape.stroke = color;
+	}
+};
+
+/**
+ * Function: drawHighlight
+ * 
+ * Creates and returns the highlight shape for the given state.
+ */
+mxCellHighlight.prototype.drawHighlight = function()
+{
+	this.shape = this.createShape();
+	this.repaint();
+
+	if (!this.keepOnTop && this.shape.node.parentNode.firstChild != this.shape.node)
+	{
+		this.shape.node.parentNode.insertBefore(this.shape.node, this.shape.node.parentNode.firstChild);
+	}
+};
+
+/**
+ * Function: createShape
+ * 
+ * Creates and returns the highlight shape for the given state.
+ */
+mxCellHighlight.prototype.createShape = function()
+{
+	var shape = this.graph.cellRenderer.createShape(this.state);
+	
+	shape.svgStrokeTolerance = this.graph.tolerance;
+	shape.points = this.state.absolutePoints;
+	shape.apply(this.state);
+	shape.stroke = this.highlightColor;
+	shape.opacity = this.opacity;
+	shape.isDashed = this.dashed;
+	shape.isShadow = false;
+	
+	shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+	shape.init(this.graph.getView().getOverlayPane());
+	mxEvent.redirectMouseEvents(shape.node, this.graph, this.state);
+	
+	if (this.graph.dialect != mxConstants.DIALECT_SVG)
+	{
+		shape.pointerEvents = false;
+	}
+	else
+	{
+		shape.svgPointerEvents = 'stroke';
+	}
+	
+	return shape;
+};
+
+/**
+ * Function: repaint
+ * 
+ * Updates the highlight after a change of the model or view.
+ */
+mxCellHighlight.prototype.getStrokeWidth = function(state)
+{
+	return this.strokeWidth;
+};
+
+/**
+ * Function: repaint
+ * 
+ * Updates the highlight after a change of the model or view.
+ */
+mxCellHighlight.prototype.repaint = function()
+{
+	if (this.state != null && this.shape != null)
+	{
+		this.shape.scale = this.state.view.scale;
+		
+		if (this.graph.model.isEdge(this.state.cell))
+		{
+			this.shape.strokewidth = this.getStrokeWidth();
+			this.shape.points = this.state.absolutePoints;
+			this.shape.outline = false;
+		}
+		else
+		{
+			this.shape.bounds = new mxRectangle(this.state.x - this.spacing, this.state.y - this.spacing,
+					this.state.width + 2 * this.spacing, this.state.height + 2 * this.spacing);
+			this.shape.rotation = Number(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+			this.shape.strokewidth = this.getStrokeWidth() / this.state.view.scale;
+			this.shape.outline = true;
+		}
+
+		// Uses cursor from shape in highlight
+		if (this.state.shape != null)
+		{
+			this.shape.setCursor(this.state.shape.getCursor());
+		}
+		
+		// Workaround for event transparency in VML with transparent color
+		// is to use a non-transparent color with near zero opacity
+		if (mxClient.IS_QUIRKS || document.documentMode == 8)
+		{
+			if (this.shape.stroke == 'transparent')
+			{
+				// KNOWN: Quirks mode does not seem to catch events if
+				// we do not force an update of the DOM via a change such
+				// as mxLog.debug. Since IE6 is EOL we do not add a fix.
+				this.shape.stroke = 'white';
+				this.shape.opacity = 1;
+			}
+			else
+			{
+				this.shape.opacity = this.opacity;
+			}
+		}
+		
+		this.shape.redraw();
+	}
+};
+
+/**
+ * Function: hide
+ * 
+ * Resets the state of the cell marker.
+ */
+mxCellHighlight.prototype.hide = function()
+{
+	this.highlight(null);
+};
+
+/**
+ * Function: mark
+ * 
+ * Marks the <markedState> and fires a <mark> event.
+ */
+mxCellHighlight.prototype.highlight = function(state)
+{
+	if (this.state != state)
+	{
+		if (this.shape != null)
+		{
+			this.shape.destroy();
+			this.shape = null;
+		}
+
+		this.state = state;
+		
+		if (this.state != null)
+		{
+			this.drawHighlight();
+		}
+	}
+};
+
+/**
+ * Function: isHighlightAt
+ * 
+ * Returns true if this highlight is at the given position.
+ */
+mxCellHighlight.prototype.isHighlightAt = function(x, y)
+{
+	var hit = false;
+	
+	// Quirks mode is currently not supported as it used a different coordinate system
+	if (this.shape != null && document.elementFromPoint != null && !mxClient.IS_QUIRKS)
+	{
+		var elt = document.elementFromPoint(x, y);
+
+		while (elt != null)
+		{
+			if (elt == this.shape.node)
+			{
+				hit = true;
+				break;
+			}
+			
+			elt = elt.parentNode;
+		}
+	}
+	
+	return hit;
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxCellHighlight.prototype.destroy = function()
+{
+	this.graph.getView().removeListener(this.resetHandler);
+	this.graph.getView().removeListener(this.repaintHandler);
+	this.graph.getModel().removeListener(this.repaintHandler);
+	
+	if (this.shape != null)
+	{
+		this.shape.destroy();
+		this.shape = null;
+	}
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/handler/mxCellMarker.js b/airavata-kubernetes/web-console/src/assets/js/handler/mxCellMarker.js
new file mode 100644
index 0000000..10037c8
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/handler/mxCellMarker.js
@@ -0,0 +1,430 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCellMarker
+ * 
+ * A helper class to process mouse locations and highlight cells.
+ * 
+ * Helper class to highlight cells. To add a cell marker to an existing graph
+ * for highlighting all cells, the following code is used:
+ * 
+ * (code)
+ * var marker = new mxCellMarker(graph);
+ * graph.addMouseListener({
+ *   mouseDown: function() {},
+ *   mouseMove: function(sender, me)
+ *   {
+ *     marker.process(me);
+ *   },
+ *   mouseUp: function() {}
+ * });
+ * (end)
+ *
+ * Event: mxEvent.MARK
+ * 
+ * Fires after a cell has been marked or unmarked. The <code>state</code>
+ * property contains the marked <mxCellState> or null if no state is marked.
+ * 
+ * Constructor: mxCellMarker
+ * 
+ * Constructs a new cell marker.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * validColor - Optional marker color for valid states. Default is
+ * <mxConstants.DEFAULT_VALID_COLOR>.
+ * invalidColor - Optional marker color for invalid states. Default is
+ * <mxConstants.DEFAULT_INVALID_COLOR>.
+ * hotspot - Portion of the width and hight where a state intersects a
+ * given coordinate pair. A value of 0 means always highlight. Default is
+ * <mxConstants.DEFAULT_HOTSPOT>.
+ */
+function mxCellMarker(graph, validColor, invalidColor, hotspot)
+{
+	mxEventSource.call(this);
+	
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.validColor = (validColor != null) ? validColor : mxConstants.DEFAULT_VALID_COLOR;
+		this.invalidColor = (validColor != null) ? invalidColor : mxConstants.DEFAULT_INVALID_COLOR;
+		this.hotspot = (hotspot != null) ? hotspot : mxConstants.DEFAULT_HOTSPOT;
+		
+		this.highlight = new mxCellHighlight(graph);
+	}
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxUtils.extend(mxCellMarker, mxEventSource);
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxCellMarker.prototype.graph = null;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if the marker is enabled. Default is true.
+ */
+mxCellMarker.prototype.enabled = true;
+
+/**
+ * Variable: hotspot
+ * 
+ * Specifies the portion of the width and height that should trigger
+ * a highlight. The area around the center of the cell to be marked is used
+ * as the hotspot. Possible values are between 0 and 1. Default is
+ * mxConstants.DEFAULT_HOTSPOT.
+ */
+mxCellMarker.prototype.hotspot = mxConstants.DEFAULT_HOTSPOT; 
+
+/**
+ * Variable: hotspotEnabled
+ * 
+ * Specifies if the hotspot is enabled. Default is false.
+ */
+mxCellMarker.prototype.hotspotEnabled = false;
+
+/**
+ * Variable: validColor
+ * 
+ * Holds the valid marker color.
+ */
+mxCellMarker.prototype.validColor = null;
+
+/**
+ * Variable: invalidColor
+ * 
+ * Holds the invalid marker color.
+ */
+mxCellMarker.prototype.invalidColor = null;
+
+/**
+ * Variable: currentColor
+ * 
+ * Holds the current marker color.
+ */
+mxCellMarker.prototype.currentColor = null;
+
+/**
+ * Variable: validState
+ * 
+ * Holds the marked <mxCellState> if it is valid.
+ */
+mxCellMarker.prototype.validState = null; 
+
+/**
+ * Variable: markedState
+ * 
+ * Holds the marked <mxCellState>.
+ */
+mxCellMarker.prototype.markedState = null;
+
+/**
+ * Function: setEnabled
+ * 
+ * Enables or disables event handling. This implementation
+ * updates <enabled>.
+ * 
+ * Parameters:
+ * 
+ * enabled - Boolean that specifies the new enabled state.
+ */
+mxCellMarker.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxCellMarker.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setHotspot
+ * 
+ * Sets the <hotspot>.
+ */
+mxCellMarker.prototype.setHotspot = function(hotspot)
+{
+	this.hotspot = hotspot;
+};
+
+/**
+ * Function: getHotspot
+ * 
+ * Returns the <hotspot>.
+ */
+mxCellMarker.prototype.getHotspot = function()
+{
+	return this.hotspot;
+};
+
+/**
+ * Function: setHotspotEnabled
+ * 
+ * Specifies whether the hotspot should be used in <intersects>.
+ */
+mxCellMarker.prototype.setHotspotEnabled = function(enabled)
+{
+	this.hotspotEnabled = enabled;
+};
+
+/**
+ * Function: isHotspotEnabled
+ * 
+ * Returns true if hotspot is used in <intersects>.
+ */
+mxCellMarker.prototype.isHotspotEnabled = function()
+{
+	return this.hotspotEnabled;
+};
+
+/**
+ * Function: hasValidState
+ * 
+ * Returns true if <validState> is not null.
+ */
+mxCellMarker.prototype.hasValidState = function()
+{
+	return this.validState != null;
+};
+
+/**
+ * Function: getValidState
+ * 
+ * Returns the <validState>.
+ */
+mxCellMarker.prototype.getValidState = function()
+{
+	return this.validState;
+};
+
+/**
+ * Function: getMarkedState
+ * 
+ * Returns the <markedState>.
+ */
+mxCellMarker.prototype.getMarkedState = function()
+{
+	return this.markedState;
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of the cell marker.
+ */
+mxCellMarker.prototype.reset = function()
+{
+	this.validState = null;
+	
+	if (this.markedState != null)
+	{
+		this.markedState = null;
+		this.unmark();
+	}
+};
+
+/**
+ * Function: process
+ * 
+ * Processes the given event and cell and marks the state returned by
+ * <getState> with the color returned by <getMarkerColor>. If the
+ * markerColor is not null, then the state is stored in <markedState>. If
+ * <isValidState> returns true, then the state is stored in <validState>
+ * regardless of the marker color. The state is returned regardless of the
+ * marker color and valid state. 
+ */
+mxCellMarker.prototype.process = function(me)
+{
+	var state = null;
+	
+	if (this.isEnabled())
+	{
+		state = this.getState(me);
+		this.setCurrentState(state, me);
+	}
+	
+	return state;
+};
+
+/**
+ * Function: setCurrentState
+ * 
+ * Sets and marks the current valid state.
+ */
+mxCellMarker.prototype.setCurrentState = function(state, me, color)
+{
+	var isValid = (state != null) ? this.isValidState(state) : false;
+	color = (color != null) ? color : this.getMarkerColor(me.getEvent(), state, isValid);
+	
+	if (isValid)
+	{
+		this.validState = state;
+	}
+	else
+	{
+		this.validState = null;
+	}
+	
+	if (state != this.markedState || color != this.currentColor)
+	{
+		this.currentColor = color;
+		
+		if (state != null && this.currentColor != null)
+		{
+			this.markedState = state;
+			this.mark();		
+		}
+		else if (this.markedState != null)
+		{
+			this.markedState = null;
+			this.unmark();
+		}
+	}
+};
+
+/**
+ * Function: markCell
+ * 
+ * Marks the given cell using the given color, or <validColor> if no color is specified.
+ */
+mxCellMarker.prototype.markCell = function(cell, color)
+{
+	var state = this.graph.getView().getState(cell);
+	
+	if (state != null)
+	{
+		this.currentColor = (color != null) ? color : this.validColor;
+		this.markedState = state;
+		this.mark();
+	}
+};
+
+/**
+ * Function: mark
+ * 
+ * Marks the <markedState> and fires a <mark> event.
+ */
+mxCellMarker.prototype.mark = function()
+{
+	this.highlight.setHighlightColor(this.currentColor);
+	this.highlight.highlight(this.markedState);
+	this.fireEvent(new mxEventObject(mxEvent.MARK, 'state', this.markedState));
+};
+
+/**
+ * Function: unmark
+ * 
+ * Hides the marker and fires a <mark> event.
+ */
+mxCellMarker.prototype.unmark = function()
+{
+	this.mark();
+};
+
+/**
+ * Function: isValidState
+ * 
+ * Returns true if the given <mxCellState> is a valid state. If this
+ * returns true, then the state is stored in <validState>. The return value
+ * of this method is used as the argument for <getMarkerColor>.
+ */
+mxCellMarker.prototype.isValidState = function(state)
+{
+	return true;
+};
+
+/**
+ * Function: getMarkerColor
+ * 
+ * Returns the valid- or invalidColor depending on the value of isValid.
+ * The given <mxCellState> is ignored by this implementation.
+ */
+mxCellMarker.prototype.getMarkerColor = function(evt, state, isValid)
+{
+	return (isValid) ? this.validColor : this.invalidColor;
+};
+
+/**
+ * Function: getState
+ * 
+ * Uses <getCell>, <getStateToMark> and <intersects> to return the
+ * <mxCellState> for the given <mxMouseEvent>.
+ */
+mxCellMarker.prototype.getState = function(me)
+{
+	var view = this.graph.getView();
+	var cell = this.getCell(me);
+	var state = this.getStateToMark(view.getState(cell));
+
+	return (state != null && this.intersects(state, me)) ? state : null;
+};
+
+/**
+ * Function: getCell
+ * 
+ * Returns the <mxCell> for the given event and cell. This returns the
+ * given cell.
+ */
+mxCellMarker.prototype.getCell = function(me)
+{
+	return me.getCell();
+};
+
+/**
+ * Function: getStateToMark
+ * 
+ * Returns the <mxCellState> to be marked for the given <mxCellState> under
+ * the mouse. This returns the given state.
+ */
+mxCellMarker.prototype.getStateToMark = function(state)
+{
+	return state;
+};
+
+/**
+ * Function: intersects
+ * 
+ * Returns true if the given coordinate pair intersects the given state.
+ * This returns true if the <hotspot> is 0 or the coordinates are inside
+ * the hotspot for the given cell state.
+ */
+mxCellMarker.prototype.intersects = function(state, me)
+{
+	if (this.hotspotEnabled)
+	{
+		return mxUtils.intersectsHotspot(state, me.getGraphX(), me.getGraphY(),
+			this.hotspot, mxConstants.MIN_HOTSPOT_SIZE,
+			mxConstants.MAX_HOTSPOT_SIZE);
+	}
+	
+	return true;
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxCellMarker.prototype.destroy = function()
+{
+	this.graph.getView().removeListener(this.resetHandler);
+	this.graph.getModel().removeListener(this.resetHandler);
+	this.highlight.destroy();
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/handler/mxCellTracker.js b/airavata-kubernetes/web-console/src/assets/js/handler/mxCellTracker.js
new file mode 100644
index 0000000..9f0c8bb
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/handler/mxCellTracker.js
@@ -0,0 +1,145 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCellTracker
+ * 
+ * Event handler that highlights cells. Inherits from <mxCellMarker>.
+ * 
+ * Example:
+ * 
+ * (code)
+ * new mxCellTracker(graph, '#00FF00');
+ * (end)
+ * 
+ * For detecting dragEnter, dragOver and dragLeave on cells, the following
+ * code can be used:
+ * 
+ * (code)
+ * graph.addMouseListener(
+ * {
+ *   cell: null,
+ *   mouseDown: function(sender, me) { },
+ *   mouseMove: function(sender, me)
+ *   {
+ *     var tmp = me.getCell();
+ *     
+ *     if (tmp != this.cell)
+ *     {
+ *       if (this.cell != null)
+ *       {
+ *         this.dragLeave(me.getEvent(), this.cell);
+ *       }
+ *       
+ *       this.cell = tmp;
+ *       
+ *       if (this.cell != null)
+ *       {
+ *         this.dragEnter(me.getEvent(), this.cell);
+ *       }
+ *     }
+ *     
+ *     if (this.cell != null)
+ *     {
+ *       this.dragOver(me.getEvent(), this.cell);
+ *     }
+ *   },
+ *   mouseUp: function(sender, me) { },
+ *   dragEnter: function(evt, cell)
+ *   {
+ *     mxLog.debug('dragEnter', cell.value);
+ *   },
+ *   dragOver: function(evt, cell)
+ *   {
+ *     mxLog.debug('dragOver', cell.value);
+ *   },
+ *   dragLeave: function(evt, cell)
+ *   {
+ *     mxLog.debug('dragLeave', cell.value);
+ *   }
+ * });
+ * (end)
+ * 
+ * Constructor: mxCellTracker
+ * 
+ * Constructs an event handler that highlights cells.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * color - Color of the highlight. Default is blue.
+ * funct - Optional JavaScript function that is used to override
+ * <mxCellMarker.getCell>.
+ */
+function mxCellTracker(graph, color, funct)
+{
+	mxCellMarker.call(this, graph, color);
+
+	this.graph.addMouseListener(this);
+	
+	if (funct != null)
+	{
+		this.getCell = funct;
+	}
+	
+	// Automatic deallocation of memory
+	if (mxClient.IS_IE)
+	{
+		mxEvent.addListener(window, 'unload', mxUtils.bind(this, function()
+		{
+			this.destroy();
+		}));
+	}
+};
+
+/**
+ * Extends mxCellMarker.
+ */
+mxUtils.extend(mxCellTracker, mxCellMarker);
+
+/**
+ * Function: mouseDown
+ * 
+ * Ignores the event. The event is not consumed.
+ */
+mxCellTracker.prototype.mouseDown = function(sender, me) { };
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by highlighting the cell under the mousepointer if it
+ * is over the hotspot region of the cell.
+ */
+mxCellTracker.prototype.mouseMove = function(sender, me)
+{
+	if (this.isEnabled())
+	{
+		this.process(me);
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by reseting the highlight.
+ */
+mxCellTracker.prototype.mouseUp = function(sender, me) { };
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the object and all its resources and DOM nodes. This doesn't
+ * normally need to be called. It is called automatically when the window
+ * unloads.
+ */
+mxCellTracker.prototype.destroy = function()
+{
+	if (!this.destroyed)
+	{
+		this.destroyed = true;
+
+		this.graph.removeMouseListener(this);
+		mxCellMarker.prototype.destroy.apply(this);
+	}
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/handler/mxConnectionHandler.js b/airavata-kubernetes/web-console/src/assets/js/handler/mxConnectionHandler.js
new file mode 100644
index 0000000..2eade4d
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/handler/mxConnectionHandler.js
@@ -0,0 +1,2204 @@
+/**
+ * Copyright (c) 2006-2016, JGraph Ltd
+ * Copyright (c) 2006-2016, Gaudenz Alder
+ */
+/**
+ * Class: mxConnectionHandler
+ *
+ * Graph event handler that creates new connections. Uses <mxTerminalMarker>
+ * for finding and highlighting the source and target vertices and
+ * <factoryMethod> to create the edge instance. This handler is built-into
+ * <mxGraph.connectionHandler> and enabled using <mxGraph.setConnectable>.
+ *
+ * Example:
+ * 
+ * (code)
+ * new mxConnectionHandler(graph, function(source, target, style)
+ * {
+ *   edge = new mxCell('', new mxGeometry());
+ *   edge.setEdge(true);
+ *   edge.setStyle(style);
+ *   edge.geometry.relative = true;
+ *   return edge;
+ * });
+ * (end)
+ * 
+ * Here is an alternative solution that just sets a specific user object for
+ * new edges by overriding <insertEdge>.
+ *
+ * (code)
+ * mxConnectionHandlerInsertEdge = mxConnectionHandler.prototype.insertEdge;
+ * mxConnectionHandler.prototype.insertEdge = function(parent, id, value, source, target, style)
+ * {
+ *   value = 'Test';
+ * 
+ *   return mxConnectionHandlerInsertEdge.apply(this, arguments);
+ * };
+ * (end)
+ * 
+ * Using images to trigger connections:
+ * 
+ * This handler uses mxTerminalMarker to find the source and target cell for
+ * the new connection and creates a new edge using <connect>. The new edge is
+ * created using <createEdge> which in turn uses <factoryMethod> or creates a
+ * new default edge.
+ * 
+ * The handler uses a "highlight-paradigm" for indicating if a cell is being
+ * used as a source or target terminal, as seen in other diagramming products.
+ * In order to allow both, moving and connecting cells at the same time,
+ * <mxConstants.DEFAULT_HOTSPOT> is used in the handler to determine the hotspot
+ * of a cell, that is, the region of the cell which is used to trigger a new
+ * connection. The constant is a value between 0 and 1 that specifies the
+ * amount of the width and height around the center to be used for the hotspot
+ * of a cell and its default value is 0.5. In addition,
+ * <mxConstants.MIN_HOTSPOT_SIZE> defines the minimum number of pixels for the
+ * width and height of the hotspot.
+ * 
+ * This solution, while standards compliant, may be somewhat confusing because
+ * there is no visual indicator for the hotspot and the highlight is seen to
+ * switch on and off while the mouse is being moved in and out. Furthermore,
+ * this paradigm does not allow to create different connections depending on
+ * the highlighted hotspot as there is only one hotspot per cell and it
+ * normally does not allow cells to be moved and connected at the same time as
+ * there is no clear indication of the connectable area of the cell.
+ * 
+ * To come across these issues, the handle has an additional <createIcons> hook
+ * with a default implementation that allows to create one icon to be used to
+ * trigger new connections. If this icon is specified, then new connections can
+ * only be created if the image is clicked while the cell is being highlighted.
+ * The <createIcons> hook may be overridden to create more than one
+ * <mxImageShape> for creating new connections, but the default implementation
+ * supports one image and is used as follows:
+ * 
+ * In order to display the "connect image" whenever the mouse is over the cell,
+ * an DEFAULT_HOTSPOT of 1 should be used:
+ * 
+ * (code)
+ * mxConstants.DEFAULT_HOTSPOT = 1;
+ * (end)
+ * 
+ * In order to avoid confusion with the highlighting, the highlight color
+ * should not be used with a connect image:
+ * 
+ * (code)
+ * mxConstants.HIGHLIGHT_COLOR = null;
+ * (end)
+ * 
+ * To install the image, the connectImage field of the mxConnectionHandler must
+ * be assigned a new <mxImage> instance:
+ * 
+ * (code)
+ * mxConnectionHandler.prototype.connectImage = new mxImage('images/green-dot.gif', 14, 14);
+ * (end)
+ * 
+ * This will use the green-dot.gif with a width and height of 14 pixels as the
+ * image to trigger new connections. In createIcons the icon field of the
+ * handler will be set in order to remember the icon that has been clicked for
+ * creating the new connection. This field will be available under selectedIcon
+ * in the connect method, which may be overridden to take the icon that
+ * triggered the new connection into account. This is useful if more than one
+ * icon may be used to create a connection.
+ *
+ * Group: Events
+ * 
+ * Event: mxEvent.START
+ * 
+ * Fires when a new connection is being created by the user. The <code>state</code>
+ * property contains the state of the source cell.
+ * 
+ * Event: mxEvent.CONNECT
+ * 
+ * Fires between begin- and endUpdate in <connect>. The <code>cell</code>
+ * property contains the inserted edge, the <code>event</code> and <code>target</code> 
+ * properties contain the respective arguments that were passed to <connect> (where
+ * target corresponds to the dropTarget argument). Finally, the <code>terminal</code>
+ * property corresponds to the target argument in <connect> or the clone of the source
+ * terminal if <createTarget> is enabled.
+ * 
+ * Note that the target is the cell under the mouse where the mouse button was released.
+ * Depending on the logic in the handler, this doesn't necessarily have to be the target
+ * of the inserted edge. To print the source, target or any optional ports IDs that the
+ * edge is connected to, the following code can be used. To get more details about the
+ * actual connection point, <mxGraph.getConnectionConstraint> can be used. To resolve
+ * the port IDs, use <mxGraphModel.getCell>.
+ * 
+ * (code)
+ * graph.connectionHandler.addListener(mxEvent.CONNECT, function(sender, evt)
+ * {
+ *   var edge = evt.getProperty('cell');
+ *   var source = graph.getModel().getTerminal(edge, true);
+ *   var target = graph.getModel().getTerminal(edge, false);
+ *   
+ *   var style = graph.getCellStyle(edge);
+ *   var sourcePortId = style[mxConstants.STYLE_SOURCE_PORT];
+ *   var targetPortId = style[mxConstants.STYLE_TARGET_PORT];
+ *   
+ *   mxLog.show();
+ *   mxLog.debug('connect', edge, source.id, target.id, sourcePortId, targetPortId);
+ * });
+ * (end)
+ *
+ * Event: mxEvent.RESET
+ * 
+ * Fires when the <reset> method is invoked.
+ *
+ * Constructor: mxConnectionHandler
+ *
+ * Constructs an event handler that connects vertices using the specified
+ * factory method to create the new edges. Modify
+ * <mxConstants.ACTIVE_REGION> to setup the region on a cell which triggers
+ * the creation of a new connection or use connect icons as explained
+ * above.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * factoryMethod - Optional function to create the edge. The function takes
+ * the source and target <mxCell> as the first and second argument and an
+ * optional cell style from the preview as the third argument. It returns
+ * the <mxCell> that represents the new edge.
+ */
+function mxConnectionHandler(graph, factoryMethod)
+{
+	mxEventSource.call(this);
+	
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.factoryMethod = factoryMethod;
+		this.init();
+		
+		// Handles escape keystrokes
+		this.escapeHandler = mxUtils.bind(this, function(sender, evt)
+		{
+			this.reset();
+		});
+		
+		this.graph.addListener(mxEvent.ESCAPE, this.escapeHandler);
+	}
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxUtils.extend(mxConnectionHandler, mxEventSource);
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxConnectionHandler.prototype.graph = null;
+
+/**
+ * Variable: factoryMethod
+ * 
+ * Function that is used for creating new edges. The function takes the
+ * source and target <mxCell> as the first and second argument and returns
+ * a new <mxCell> that represents the edge. This is used in <createEdge>.
+ */
+mxConnectionHandler.prototype.factoryMethod = true;
+
+/**
+ * Variable: moveIconFront
+ * 
+ * Specifies if icons should be displayed inside the graph container instead
+ * of the overlay pane. This is used for HTML labels on vertices which hide
+ * the connect icon. This has precendence over <moveIconBack> when set
+ * to true. Default is false.
+ */
+mxConnectionHandler.prototype.moveIconFront = false;
+
+/**
+ * Variable: moveIconBack
+ * 
+ * Specifies if icons should be moved to the back of the overlay pane. This can
+ * be set to true if the icons of the connection handler conflict with other
+ * handles, such as the vertex label move handle. Default is false.
+ */
+mxConnectionHandler.prototype.moveIconBack = false;
+
+/**
+ * Variable: connectImage
+ * 
+ * <mxImage> that is used to trigger the creation of a new connection. This
+ * is used in <createIcons>. Default is null.
+ */
+mxConnectionHandler.prototype.connectImage = null;
+
+/**
+ * Variable: targetConnectImage
+ * 
+ * Specifies if the connect icon should be centered on the target state
+ * while connections are being previewed. Default is false.
+ */
+mxConnectionHandler.prototype.targetConnectImage = false;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxConnectionHandler.prototype.enabled = true;
+
+/**
+ * Variable: select
+ * 
+ * Specifies if new edges should be selected. Default is true.
+ */
+mxConnectionHandler.prototype.select = true;
+
+/**
+ * Variable: createTarget
+ * 
+ * Specifies if <createTargetVertex> should be called if no target was under the
+ * mouse for the new connection. Setting this to true means the connection
+ * will be drawn as valid if no target is under the mouse, and
+ * <createTargetVertex> will be called before the connection is created between
+ * the source cell and the newly created vertex in <createTargetVertex>, which
+ * can be overridden to create a new target. Default is false.
+ */
+mxConnectionHandler.prototype.createTarget = false;
+
+/**
+ * Variable: marker
+ * 
+ * Holds the <mxTerminalMarker> used for finding source and target cells.
+ */
+mxConnectionHandler.prototype.marker = null;
+
+/**
+ * Variable: constraintHandler
+ * 
+ * Holds the <mxConstraintHandler> used for drawing and highlighting
+ * constraints.
+ */
+mxConnectionHandler.prototype.constraintHandler = null;
+
+/**
+ * Variable: error
+ * 
+ * Holds the current validation error while connections are being created.
+ */
+mxConnectionHandler.prototype.error = null;
+
+/**
+ * Variable: waypointsEnabled
+ * 
+ * Specifies if single clicks should add waypoints on the new edge. Default is
+ * false.
+ */
+mxConnectionHandler.prototype.waypointsEnabled = false;
+
+/**
+ * Variable: ignoreMouseDown
+ * 
+ * Specifies if the connection handler should ignore the state of the mouse
+ * button when highlighting the source. Default is false, that is, the
+ * handler only highlights the source if no button is being pressed.
+ */
+mxConnectionHandler.prototype.ignoreMouseDown = false;
+
+/**
+ * Variable: first
+ * 
+ * Holds the <mxPoint> where the mouseDown took place while the handler is
+ * active.
+ */
+mxConnectionHandler.prototype.first = null;
+
+/**
+ * Variable: connectIconOffset
+ * 
+ * Holds the offset for connect icons during connection preview.
+ * Default is mxPoint(0, <mxConstants.TOOLTIP_VERTICAL_OFFSET>).
+ * Note that placing the icon under the mouse pointer with an
+ * offset of (0,0) will affect hit detection.
+ */
+mxConnectionHandler.prototype.connectIconOffset = new mxPoint(0, mxConstants.TOOLTIP_VERTICAL_OFFSET);
+
+/**
+ * Variable: edgeState
+ * 
+ * Optional <mxCellState> that represents the preview edge while the
+ * handler is active. This is created in <createEdgeState>.
+ */
+mxConnectionHandler.prototype.edgeState = null;
+
+/**
+ * Variable: changeHandler
+ * 
+ * Holds the change event listener for later removal.
+ */
+mxConnectionHandler.prototype.changeHandler = null;
+
+/**
+ * Variable: drillHandler
+ * 
+ * Holds the drill event listener for later removal.
+ */
+mxConnectionHandler.prototype.drillHandler = null;
+
+/**
+ * Variable: mouseDownCounter
+ * 
+ * Counts the number of mouseDown events since the start. The initial mouse
+ * down event counts as 1.
+ */
+mxConnectionHandler.prototype.mouseDownCounter = 0;
+
+/**
+ * Variable: movePreviewAway
+ * 
+ * Switch to enable moving the preview away from the mousepointer. This is required in browsers
+ * where the preview cannot be made transparent to events and if the built-in hit detection on
+ * the HTML elements in the page should be used. Default is the value of <mxClient.IS_VML>.
+ */
+mxConnectionHandler.prototype.movePreviewAway = mxClient.IS_VML;
+
+/**
+ * Variable: outlineConnect
+ * 
+ * Specifies if connections to the outline of a highlighted target should be
+ * enabled. This will allow to place the connection point along the outline of
+ * the highlighted target. Default is false.
+ */
+mxConnectionHandler.prototype.outlineConnect = false;
+
+/**
+ * Variable: livePreview
+ * 
+ * Specifies if the actual shape of the edge state should be used for the preview.
+ * Default is false. (Ignored if no edge state is created in <createEdgeState>.)
+ */
+mxConnectionHandler.prototype.livePreview = false;
+
+/**
+ * Variable: cursor
+ * 
+ * Specifies the cursor to be used while the handler is active. Default is null.
+ */
+mxConnectionHandler.prototype.cursor = null;
+
+/**
+ * Variable: insertBeforeSource
+ * 
+ * Specifies if new edges should be inserted before the source vertex in the
+ * cell hierarchy. Default is false for backwards compatibility.
+ */
+mxConnectionHandler.prototype.insertBeforeSource = false;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxConnectionHandler.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+	
+/**
+ * Function: setEnabled
+ * 
+ * Enables or disables event handling. This implementation
+ * updates <enabled>.
+ * 
+ * Parameters:
+ * 
+ * enabled - Boolean that specifies the new enabled state.
+ */
+mxConnectionHandler.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: isInsertBefore
+ * 
+ * Returns <insertBeforeSource> for non-loops and false for loops.
+ *
+ * Parameters:
+ * 
+ * edge - <mxCell> that represents the edge to be inserted.
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ * evt - Mousedown event of the connect gesture.
+ * dropTarget - <mxCell> that represents the cell under the mouse when it was
+ * released.
+ */
+mxConnectionHandler.prototype.isInsertBefore = function(edge, source, target, evt, dropTarget)
+{
+	return this.insertBeforeSource && source != target;
+};
+
+/**
+ * Function: isCreateTarget
+ * 
+ * Returns <createTarget>.
+ *
+ * Parameters:
+ *
+ * evt - Current active native pointer event.
+ */
+mxConnectionHandler.prototype.isCreateTarget = function(evt)
+{
+	return this.createTarget;
+};
+
+/**
+ * Function: setCreateTarget
+ * 
+ * Sets <createTarget>.
+ */
+mxConnectionHandler.prototype.setCreateTarget = function(value)
+{
+	this.createTarget = value;
+};
+
+/**
+ * Function: createShape
+ * 
+ * Creates the preview shape for new connections.
+ */
+mxConnectionHandler.prototype.createShape = function()
+{
+	// Creates the edge preview
+	var shape = (this.livePreview && this.edgeState != null) ?
+		this.graph.cellRenderer.createShape(this.edgeState) :
+		new mxPolyline([], mxConstants.INVALID_COLOR);
+	shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+		mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+	shape.scale = this.graph.view.scale;
+	shape.pointerEvents = false;
+	shape.isDashed = true;
+	shape.init(this.graph.getView().getOverlayPane());
+	mxEvent.redirectMouseEvents(shape.node, this.graph, null);
+
+	return shape;
+};
+
+/**
+ * Function: init
+ * 
+ * Initializes the shapes required for this connection handler. This should
+ * be invoked if <mxGraph.container> is assigned after the connection
+ * handler has been created.
+ */
+mxConnectionHandler.prototype.init = function()
+{
+	this.graph.addMouseListener(this);
+	this.marker = this.createMarker();
+	this.constraintHandler = new mxConstraintHandler(this.graph);
+
+	// Redraws the icons if the graph changes
+	this.changeHandler = mxUtils.bind(this, function(sender)
+	{
+		if (this.iconState != null)
+		{
+			this.iconState = this.graph.getView().getState(this.iconState.cell);
+		}
+		
+		if (this.iconState != null)
+		{
+			this.redrawIcons(this.icons, this.iconState);
+			this.constraintHandler.reset();
+		}
+		else if (this.previous != null && this.graph.view.getState(this.previous.cell) == null)
+		{
+			this.reset();
+		}
+	});
+	
+	this.graph.getModel().addListener(mxEvent.CHANGE, this.changeHandler);
+	this.graph.getView().addListener(mxEvent.SCALE, this.changeHandler);
+	this.graph.getView().addListener(mxEvent.TRANSLATE, this.changeHandler);
+	this.graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, this.changeHandler);
+	
+	// Removes the icon if we step into/up or start editing
+	this.drillHandler = mxUtils.bind(this, function(sender)
+	{
+		this.reset();
+	});
+	
+	this.graph.addListener(mxEvent.START_EDITING, this.drillHandler);
+	this.graph.getView().addListener(mxEvent.DOWN, this.drillHandler);
+	this.graph.getView().addListener(mxEvent.UP, this.drillHandler);
+};
+
+/**
+ * Function: isConnectableCell
+ * 
+ * Returns true if the given cell is connectable. This is a hook to
+ * disable floating connections. This implementation returns true.
+ */
+mxConnectionHandler.prototype.isConnectableCell = function(cell)
+{
+	return true;
+};
+
+/**
+ * Function: createMarker
+ * 
+ * Creates and returns the <mxCellMarker> used in <marker>.
+ */
+mxConnectionHandler.prototype.createMarker = function()
+{
+	var marker = new mxCellMarker(this.graph);
+	marker.hotspotEnabled = true;
+
+	// Overrides to return cell at location only if valid (so that
+	// there is no highlight for invalid cells)
+	marker.getCell = mxUtils.bind(this, function(me)
+	{
+		var cell = mxCellMarker.prototype.getCell.apply(marker, arguments);
+		this.error = null;
+		
+		// Checks for cell at preview point (with grid)
+		if (cell == null && this.currentPoint != null)
+		{
+			cell = this.graph.getCellAt(this.currentPoint.x, this.currentPoint.y);
+		}
+		
+		// Uses connectable parent vertex if one exists
+		if (cell != null && !this.graph.isCellConnectable(cell))
+		{
+			var parent = this.graph.getModel().getParent(cell);
+			
+			if (this.graph.getModel().isVertex(parent) && this.graph.isCellConnectable(parent))
+			{
+				cell = parent;
+			}
+		}
+		
+		if ((this.graph.isSwimlane(cell) && this.currentPoint != null &&
+			this.graph.hitsSwimlaneContent(cell, this.currentPoint.x, this.currentPoint.y)) ||
+			!this.isConnectableCell(cell))
+		{
+			cell = null;
+		}
+		
+		if (cell != null)
+		{
+			if (this.isConnecting())
+			{
+				if (this.previous != null)
+				{
+					this.error = this.validateConnection(this.previous.cell, cell);
+					
+					if (this.error != null && this.error.length == 0)
+					{
+						cell = null;
+						
+						// Enables create target inside groups
+						if (this.isCreateTarget(me.getEvent()))
+						{
+							this.error = null;
+						}
+					}
+				}
+			}
+			else if (!this.isValidSource(cell, me))
+			{
+				cell = null;
+			}
+		}
+		else if (this.isConnecting() && !this.isCreateTarget(me.getEvent()) &&
+				!this.graph.allowDanglingEdges)
+		{
+			this.error = '';
+		}
+
+		return cell;
+	});
+
+	// Sets the highlight color according to validateConnection
+	marker.isValidState = mxUtils.bind(this, function(state)
+	{
+		if (this.isConnecting())
+		{
+			return this.error == null;
+		}
+		else
+		{
+			return mxCellMarker.prototype.isValidState.apply(marker, arguments);
+		}
+	});
+
+	// Overrides to use marker color only in highlight mode or for
+	// target selection
+	marker.getMarkerColor = mxUtils.bind(this, function(evt, state, isValid)
+	{
+		return (this.connectImage == null || this.isConnecting()) ?
+			mxCellMarker.prototype.getMarkerColor.apply(marker, arguments) :
+			null;
+	});
+
+	// Overrides to use hotspot only for source selection otherwise
+	// intersects always returns true when over a cell
+	marker.intersects = mxUtils.bind(this, function(state, evt)
+	{
+		if (this.connectImage != null || this.isConnecting())
+		{
+			return true;
+		}
+		
+		return mxCellMarker.prototype.intersects.apply(marker, arguments);
+	});
+
+	return marker;
+};
+
+/**
+ * Function: start
+ * 
+ * Starts a new connection for the given state and coordinates.
+ */
+mxConnectionHandler.prototype.start = function(state, x, y, edgeState)
+{
+	this.previous = state;
+	this.first = new mxPoint(x, y);
+	this.edgeState = (edgeState != null) ? edgeState : this.createEdgeState(null);
+	
+	// Marks the source state
+	this.marker.currentColor = this.marker.validColor;
+	this.marker.markedState = state;
+	this.marker.mark();
+
+	this.fireEvent(new mxEventObject(mxEvent.START, 'state', this.previous));
+};
+
+/**
+ * Function: isConnecting
+ * 
+ * Returns true if the source terminal has been clicked and a new
+ * connection is currently being previewed.
+ */
+mxConnectionHandler.prototype.isConnecting = function()
+{
+	return this.first != null && this.shape != null;
+};
+
+/**
+ * Function: isValidSource
+ * 
+ * Returns <mxGraph.isValidSource> for the given source terminal.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the source terminal.
+ * me - <mxMouseEvent> that is associated with this call.
+ */
+mxConnectionHandler.prototype.isValidSource = function(cell, me)
+{
+	return this.graph.isValidSource(cell);
+};
+
+/**
+ * Function: isValidTarget
+ * 
+ * Returns true. The call to <mxGraph.isValidTarget> is implicit by calling
+ * <mxGraph.getEdgeValidationError> in <validateConnection>. This is an
+ * additional hook for disabling certain targets in this specific handler.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the target terminal.
+ */
+mxConnectionHandler.prototype.isValidTarget = function(cell)
+{
+	return true;
+};
+
+/**
+ * Function: validateConnection
+ * 
+ * Returns the error message or an empty string if the connection for the
+ * given source target pair is not valid. Otherwise it returns null. This
+ * implementation uses <mxGraph.getEdgeValidationError>.
+ * 
+ * Parameters:
+ * 
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ */
+mxConnectionHandler.prototype.validateConnection = function(source, target)
+{
+	if (!this.isValidTarget(target))
+	{
+		return '';
+	}
+	
+	return this.graph.getEdgeValidationError(null, source, target);
+};
+
+/**
+ * Function: getConnectImage
+ * 
+ * Hook to return the <mxImage> used for the connection icon of the given
+ * <mxCellState>. This implementation returns <connectImage>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose connect image should be returned.
+ */
+mxConnectionHandler.prototype.getConnectImage = function(state)
+{
+	return this.connectImage;
+};
+
+/**
+ * Function: isMoveIconToFrontForState
+ * 
+ * Returns true if the state has a HTML label in the graph's container, otherwise
+ * it returns <moveIconFront>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose connect icons should be returned.
+ */
+mxConnectionHandler.prototype.isMoveIconToFrontForState = function(state)
+{
+	if (state.text != null && state.text.node.parentNode == this.graph.container)
+	{
+		return true;
+	}
+	
+	return this.moveIconFront;
+};
+
+/**
+ * Function: createIcons
+ * 
+ * Creates the array <mxImageShapes> that represent the connect icons for
+ * the given <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> whose connect icons should be returned.
+ */
+mxConnectionHandler.prototype.createIcons = function(state)
+{
+	var image = this.getConnectImage(state);
+	
+	if (image != null && state != null)
+	{
+		this.iconState = state;
+		var icons = [];
+
+		// Cannot use HTML for the connect icons because the icon receives all
+		// mouse move events in IE, must use VML and SVG instead even if the
+		// connect-icon appears behind the selection border and the selection
+		// border consumes the events before the icon gets a chance
+		var bounds = new mxRectangle(0, 0, image.width, image.height);
+		var icon = new mxImageShape(bounds, image.src, null, null, 0);
+		icon.preserveImageAspect = false;
+		
+		if (this.isMoveIconToFrontForState(state))
+		{
+			icon.dialect = mxConstants.DIALECT_STRICTHTML;
+			icon.init(this.graph.container);
+		}
+		else
+		{
+			icon.dialect = (this.graph.dialect == mxConstants.DIALECT_SVG) ?
+				mxConstants.DIALECT_SVG : mxConstants.DIALECT_VML;
+			icon.init(this.graph.getView().getOverlayPane());
+
+			// Move the icon back in the overlay pane
+			if (this.moveIconBack && icon.node.previousSibling != null)
+			{
+				icon.node.parentNode.insertBefore(icon.node, icon.node.parentNode.firstChild);
+			}
+		}
+
+		icon.node.style.cursor = mxConstants.CURSOR_CONNECT;
+
+		// Events transparency
+		var getState = mxUtils.bind(this, function()
+		{
+			return (this.currentState != null) ? this.currentState : state;
+		});
+		
+		// Updates the local icon before firing the mouse down event.
+		var mouseDown = mxUtils.bind(this, function(evt)
+		{
+			if (!mxEvent.isConsumed(evt))
+			{
+				this.icon = icon;
+				this.graph.fireMouseEvent(mxEvent.MOUSE_DOWN,
+					new mxMouseEvent(evt, getState()));
+			}
+		});
+
+		mxEvent.redirectMouseEvents(icon.node, this.graph, getState, mouseDown);
+		
+		icons.push(icon);
+		this.redrawIcons(icons, this.iconState);
+		
+		return icons;
+	}
+	
+	return null;
+};
+
+/**
+ * Function: redrawIcons
+ * 
+ * Redraws the given array of <mxImageShapes>.
+ * 
+ * Parameters:
+ * 
+ * icons - Optional array of <mxImageShapes> to be redrawn.
+ */
+mxConnectionHandler.prototype.redrawIcons = function(icons, state)
+{
+	if (icons != null && icons[0] != null && state != null)
+	{
+		var pos = this.getIconPosition(icons[0], state);
+		icons[0].bounds.x = pos.x;
+		icons[0].bounds.y = pos.y;
+		icons[0].redraw();
+	}
+};
+
+/**
+ * Function: redrawIcons
+ * 
+ * Redraws the given array of <mxImageShapes>.
+ * 
+ * Parameters:
+ * 
+ * icons - Optional array of <mxImageShapes> to be redrawn.
+ */
+mxConnectionHandler.prototype.getIconPosition = function(icon, state)
+{
+	var scale = this.graph.getView().scale;
+	var cx = state.getCenterX();
+	var cy = state.getCenterY();
+	
+	if (this.graph.isSwimlane(state.cell))
+	{
+		var size = this.graph.getStartSize(state.cell);
+		
+		cx = (size.width != 0) ? state.x + size.width * scale / 2 : cx;
+		cy = (size.height != 0) ? state.y + size.height * scale / 2 : cy;
+		
+		var alpha = mxUtils.toRadians(mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION) || 0);
+		
+		if (alpha != 0)
+		{
+			var cos = Math.cos(alpha);
+			var sin = Math.sin(alpha);
+			var ct = new mxPoint(state.getCenterX(), state.getCenterY());
+			var pt = mxUtils.getRotatedPoint(new mxPoint(cx, cy), cos, sin, ct);
+			cx = pt.x;
+			cy = pt.y;
+		}
+	}
+
+	return new mxPoint(cx - icon.bounds.width / 2,
+			cy - icon.bounds.height / 2);
+};
+
+/**
+ * Function: destroyIcons
+ * 
+ * Destroys the connect icons and resets the respective state.
+ */
+mxConnectionHandler.prototype.destroyIcons = function()
+{
+	if (this.icons != null)
+	{
+		for (var i = 0; i < this.icons.length; i++)
+		{
+			this.icons[i].destroy();
+		}
+		
+		this.icons = null;
+		this.icon = null;
+		this.selectedIcon = null;
+		this.iconState = null;
+	}
+};
+
+/**
+ * Function: isStartEvent
+ * 
+ * Returns true if the given mouse down event should start this handler. The
+ * This implementation returns true if the event does not force marquee
+ * selection, and the currentConstraint and currentFocus of the
+ * <constraintHandler> are not null, or <previous> and <error> are not null and
+ * <icons> is null or <icons> and <icon> are not null.
+ */
+mxConnectionHandler.prototype.isStartEvent = function(me)
+{
+	return ((this.constraintHandler.currentFocus != null && this.constraintHandler.currentConstraint != null) ||
+		(this.previous != null && this.error == null && (this.icons == null || (this.icons != null &&
+		this.icon != null))));
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by initiating a new connection.
+ */
+mxConnectionHandler.prototype.mouseDown = function(sender, me)
+{
+	this.mouseDownCounter++;
+	
+	if (this.isEnabled() && this.graph.isEnabled() && !me.isConsumed() &&
+		!this.isConnecting() && this.isStartEvent(me))
+	{
+		if (this.constraintHandler.currentConstraint != null &&
+			this.constraintHandler.currentFocus != null &&
+			this.constraintHandler.currentPoint != null)
+		{
+			this.sourceConstraint = this.constraintHandler.currentConstraint;
+			this.previous = this.constraintHandler.currentFocus;
+			this.first = this.constraintHandler.currentPoint.clone();
+		}
+		else
+		{
+			// Stores the location of the initial mousedown
+			this.first = new mxPoint(me.getGraphX(), me.getGraphY());
+		}
+	
+		this.edgeState = this.createEdgeState(me);
+		this.mouseDownCounter = 1;
+		
+		if (this.waypointsEnabled && this.shape == null)
+		{
+			this.waypoints = null;
+			this.shape = this.createShape();
+			
+			if (this.edgeState != null)
+			{
+				this.shape.apply(this.edgeState);
+			}
+		}
+
+		// Stores the starting point in the geometry of the preview
+		if (this.previous == null && this.edgeState != null)
+		{
+			var pt = this.graph.getPointForEvent(me.getEvent());
+			this.edgeState.cell.geometry.setTerminalPoint(pt, true);
+		}
+		
+		this.fireEvent(new mxEventObject(mxEvent.START, 'state', this.previous));
+
+		me.consume();
+	}
+
+	this.selectedIcon = this.icon;
+	this.icon = null;
+};
+
+/**
+ * Function: isImmediateConnectSource
+ * 
+ * Returns true if a tap on the given source state should immediately start
+ * connecting. This implementation returns true if the state is not movable
+ * in the graph. 
+ */
+mxConnectionHandler.prototype.isImmediateConnectSource = function(state)
+{
+	return !this.graph.isCellMovable(state.cell);
+};
+
+/**
+ * Function: createEdgeState
+ * 
+ * Hook to return an <mxCellState> which may be used during the preview.
+ * This implementation returns null.
+ * 
+ * Use the following code to create a preview for an existing edge style:
+ * 
+ * (code)
+ * graph.connectionHandler.createEdgeState = function(me)
+ * {
+ *   var edge = graph.createEdge(null, null, null, null, null, 'edgeStyle=elbowEdgeStyle');
+ *   
+ *   return new mxCellState(this.graph.view, edge, this.graph.getCellStyle(edge));
+ * };
+ * (end)
+ */
+mxConnectionHandler.prototype.createEdgeState = function(me)
+{
+	return null;
+};
+
+/**
+ * Function: isOutlineConnectEvent
+ * 
+ * Returns true if <outlineConnect> is true and the source of the event is the outline shape
+ * or shift is pressed.
+ */
+mxConnectionHandler.prototype.isOutlineConnectEvent = function(me)
+{
+	var offset = mxUtils.getOffset(this.graph.container);
+	var evt = me.getEvent();
+	
+	var clientX = mxEvent.getClientX(evt);
+	var clientY = mxEvent.getClientY(evt);
+	
+	var doc = document.documentElement;
+	var left = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
+	var top = (window.pageYOffset || doc.scrollTop)  - (doc.clientTop || 0);
+	
+	var gridX = this.currentPoint.x - this.graph.container.scrollLeft + offset.x - left;
+	var gridY = this.currentPoint.y - this.graph.container.scrollTop + offset.y - top;
+
+	return this.outlineConnect && !mxEvent.isShiftDown(me.getEvent()) &&
+		(me.isSource(this.marker.highlight.shape) ||
+		(mxEvent.isAltDown(me.getEvent()) && me.getState() != null) ||
+		this.marker.highlight.isHighlightAt(clientX, clientY) ||
+		((gridX != clientX || gridY != clientY) && me.getState() == null &&
+		this.marker.highlight.isHighlightAt(gridX, gridY)));
+};
+
+/**
+ * Function: updateCurrentState
+ * 
+ * Updates the current state for a given mouse move event by using
+ * the <marker>.
+ */
+mxConnectionHandler.prototype.updateCurrentState = function(me, point)
+{
+	this.constraintHandler.update(me, this.first == null, false, (this.first == null ||
+		me.isSource(this.marker.highlight.shape)) ? null : point);
+	
+	if (this.constraintHandler.currentFocus != null && this.constraintHandler.currentConstraint != null)
+	{
+		// Handles special case where grid is large and connection point is at actual point in which
+		// case the outline is not followed as long as we're < gridSize / 2 away from that point
+		if (this.marker.highlight != null && this.marker.highlight.state != null &&
+			this.marker.highlight.state.cell == this.constraintHandler.currentFocus.cell)
+		{
+			// Direct repaint needed if cell already highlighted
+			if (this.marker.highlight.shape.stroke != 'transparent')
+			{
+				this.marker.highlight.shape.stroke = 'transparent';
+				this.marker.highlight.repaint();
+			}
+		}
+		else
+		{
+			this.marker.markCell(this.constraintHandler.currentFocus.cell, 'transparent');
+		}
+
+		// Updates validation state
+		if (this.previous != null)
+		{
+			this.error = this.validateConnection(this.previous.cell, this.constraintHandler.currentFocus.cell);
+			
+			if (this.error == null)
+			{
+				this.currentState = this.constraintHandler.currentFocus;
+			}
+			else
+			{
+				this.constraintHandler.reset();
+			}
+		}
+	}
+	else
+	{
+		if (this.graph.isIgnoreTerminalEvent(me.getEvent()))
+		{
+			this.marker.reset();
+			this.currentState = null;
+		}
+		else
+		{
+			this.marker.process(me);
+			this.currentState = this.marker.getValidState();
+		}
+
+		var outline = this.isOutlineConnectEvent(me);
+		
+		if (this.currentState != null && outline)
+		{
+			// Handles special case where mouse is on outline away from actual end point
+			// in which case the grid is ignored and mouse point is used instead
+			if (me.isSource(this.marker.highlight.shape))
+			{
+				point = new mxPoint(me.getGraphX(), me.getGraphY());
+			}
+			
+			var constraint = this.graph.getOutlineConstraint(point, this.currentState, me);
+			this.constraintHandler.setFocus(me, this.currentState, false);
+			this.constraintHandler.currentConstraint = constraint;
+			this.constraintHandler.currentPoint = point;
+		}
+
+		if (this.outlineConnect)
+		{
+			if (this.marker.highlight != null && this.marker.highlight.shape != null)
+			{
+				var s = this.graph.view.scale;
+				
+				if (this.constraintHandler.currentConstraint != null &&
+					this.constraintHandler.currentFocus != null)
+				{
+					this.marker.highlight.shape.stroke = mxConstants.OUTLINE_HIGHLIGHT_COLOR;
+					this.marker.highlight.shape.strokewidth = mxConstants.OUTLINE_HIGHLIGHT_STROKEWIDTH / s / s;
+					this.marker.highlight.repaint();
+				} 
+				else if (this.marker.hasValidState())
+				{
+					// Handles special case where actual end point of edge and current mouse point
+					// are not equal (due to grid snapping) and there is no hit on shape or highlight
+					if (this.marker.getValidState() != me.getState())
+					{
+						this.marker.highlight.shape.stroke = 'transparent';
+						this.currentState = null;
+					}
+					else
+					{
+						this.marker.highlight.shape.stroke = mxConstants.DEFAULT_VALID_COLOR;
+					}
+	
+					this.marker.highlight.shape.strokewidth = mxConstants.HIGHLIGHT_STROKEWIDTH / s / s;
+					this.marker.highlight.repaint();
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: convertWaypoint
+ * 
+ * Converts the given point from screen coordinates to model coordinates.
+ */
+mxConnectionHandler.prototype.convertWaypoint = function(point)
+{
+	var scale = this.graph.getView().getScale();
+	var tr = this.graph.getView().getTranslate();
+	
+	point.x = point.x / scale - tr.x;
+	point.y = point.y / scale - tr.y;
+};
+
+/**
+ * Function: snapToPreview
+ * 
+ * Called to snap the given point to the current preview. This snaps to the
+ * first point of the preview if alt is not pressed.
+ */
+mxConnectionHandler.prototype.snapToPreview = function(me, point)
+{
+	if (!mxEvent.isAltDown(me.getEvent()) && this.previous != null)
+	{
+		var tol = this.graph.gridSize * this.graph.view.scale / 2;	
+		var tmp = (this.sourceConstraint != null) ? this.first :
+			new mxPoint(this.previous.getCenterX(), this.previous.getCenterY());
+
+		if (Math.abs(tmp.x - me.getGraphX()) < tol)
+		{
+			point.x = tmp.x;
+		}
+		
+		if (Math.abs(tmp.y - me.getGraphY()) < tol)
+		{
+			point.y = tmp.y;
+		}
+	}	
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating the preview edge or by highlighting
+ * a possible source or target terminal.
+ */
+mxConnectionHandler.prototype.mouseMove = function(sender, me)
+{
+	if (!me.isConsumed() && (this.ignoreMouseDown || this.first != null || !this.graph.isMouseDown))
+	{
+		// Handles special case when handler is disabled during highlight
+		if (!this.isEnabled() && this.currentState != null)
+		{
+			this.destroyIcons();
+			this.currentState = null;
+		}
+
+		var view = this.graph.getView();
+		var scale = view.scale;
+		var tr = view.translate;
+		var point = new mxPoint(me.getGraphX(), me.getGraphY());
+		this.error = null;
+
+		if (this.graph.isGridEnabledEvent(me.getEvent()))
+		{
+			point = new mxPoint((this.graph.snap(point.x / scale - tr.x) + tr.x) * scale,
+				(this.graph.snap(point.y / scale - tr.y) + tr.y) * scale);
+		}
+		
+		this.snapToPreview(me, point);
+		this.currentPoint = point;
+		
+		if (this.first != null || (this.isEnabled() && this.graph.isEnabled()))
+		{
+			this.updateCurrentState(me, point);
+		}
+
+		if (this.first != null)
+		{
+			var constraint = null;
+			var current = point;
+			
+			// Uses the current point from the constraint handler if available
+			if (this.constraintHandler.currentConstraint != null &&
+				this.constraintHandler.currentFocus != null &&
+				this.constraintHandler.currentPoint != null)
+			{
+				constraint = this.constraintHandler.currentConstraint;
+				current = this.constraintHandler.currentPoint.clone();
+			}
+			else if (this.previous != null && !this.graph.isIgnoreTerminalEvent(me.getEvent()) && mxEvent.isShiftDown(me.getEvent()))
+			{
+				if (Math.abs(this.previous.getCenterX() - point.x) < Math.abs(this.previous.getCenterY() - point.y))
+				{
+					point.x = this.previous.getCenterX();
+				}
+				else
+				{
+					point.y = this.previous.getCenterY();
+				}
+			}
+			
+			var pt2 = this.first;
+			
+			// Moves the connect icon with the mouse
+			if (this.selectedIcon != null)
+			{
+				var w = this.selectedIcon.bounds.width;
+				var h = this.selectedIcon.bounds.height;
+				
+				if (this.currentState != null && this.targetConnectImage)
+				{
+					var pos = this.getIconPosition(this.selectedIcon, this.currentState);
+					this.selectedIcon.bounds.x = pos.x;
+					this.selectedIcon.bounds.y = pos.y;
+				}
+				else
+				{
+					var bounds = new mxRectangle(me.getGraphX() + this.connectIconOffset.x,
+						me.getGraphY() + this.connectIconOffset.y, w, h);
+					this.selectedIcon.bounds = bounds;
+				}
+				
+				this.selectedIcon.redraw();
+			}
+
+			// Uses edge state to compute the terminal points
+			if (this.edgeState != null)
+			{
+				this.updateEdgeState(current, constraint);
+				current = this.edgeState.absolutePoints[this.edgeState.absolutePoints.length - 1];
+				pt2 = this.edgeState.absolutePoints[0];
+			}
+			else
+			{
+				if (this.currentState != null)
+				{
+					if (this.constraintHandler.currentConstraint == null)
+					{
+						var tmp = this.getTargetPerimeterPoint(this.currentState, me);
+						
+						if (tmp != null)
+						{
+							current = tmp;
+						}
+					}
+				}
+				
+				// Computes the source perimeter point
+				if (this.sourceConstraint == null && this.previous != null)
+				{
+					var next = (this.waypoints != null && this.waypoints.length > 0) ?
+							this.waypoints[0] : current;
+					var tmp = this.getSourcePerimeterPoint(this.previous, next, me);
+					
+					if (tmp != null)
+					{
+						pt2 = tmp;
+					}
+				}
+			}
+
+			// Makes sure the cell under the mousepointer can be detected
+			// by moving the preview shape away from the mouse. This
+			// makes sure the preview shape does not prevent the detection
+			// of the cell under the mousepointer even for slow gestures.
+			if (this.currentState == null && this.movePreviewAway)
+			{
+				var tmp = pt2; 
+				
+				if (this.edgeState != null && this.edgeState.absolutePoints.length >= 2)
+				{
+					var tmp2 = this.edgeState.absolutePoints[this.edgeState.absolutePoints.length - 2];
+					
+					if (tmp2 != null)
+					{
+						tmp = tmp2;
+					}
+				}
+				
+				var dx = current.x - tmp.x;
+				var dy = current.y - tmp.y;
+				
+				var len = Math.sqrt(dx * dx + dy * dy);
+				
+				if (len == 0)
+				{
+					return;
+				}
+
+				// Stores old point to reuse when creating edge
+				this.originalPoint = current.clone();
+				current.x -= dx * 4 / len;
+				current.y -= dy * 4 / len;
+			}
+			else
+			{
+				this.originalPoint = null;
+			}
+			
+			// Creates the preview shape (lazy)
+			if (this.shape == null)
+			{
+				var dx = Math.abs(point.x - this.first.x);
+				var dy = Math.abs(point.y - this.first.y);
+
+				if (dx > this.graph.tolerance || dy > this.graph.tolerance)
+				{
+					this.shape = this.createShape();
+
+					if (this.edgeState != null)
+					{
+						this.shape.apply(this.edgeState);
+					}
+					
+					// Revalidates current connection
+					this.updateCurrentState(me, point);
+				}
+			}
+
+			// Updates the points in the preview edge
+			if (this.shape != null)
+			{
+				if (this.edgeState != null)
+				{
+					this.shape.points = this.edgeState.absolutePoints;
+				}
+				else
+				{
+					var pts = [pt2];
+					
+					if (this.waypoints != null)
+					{
+						pts = pts.concat(this.waypoints);
+					}
+
+					pts.push(current);
+					this.shape.points = pts;
+				}
+				
+				this.drawPreview();
+			}
+			
+			// Makes sure endpoint of edge is visible during connect
+			if (this.cursor != null)
+			{
+				this.graph.container.style.cursor = this.cursor;
+			}
+			
+			mxEvent.consume(me.getEvent());
+			me.consume();
+		}
+		else if (!this.isEnabled() || !this.graph.isEnabled())
+		{
+			this.constraintHandler.reset();
+		}
+		else if (this.previous != this.currentState && this.edgeState == null)
+		{
+			this.destroyIcons();
+			
+			// Sets the cursor on the current shape				
+			if (this.currentState != null && this.error == null && this.constraintHandler.currentConstraint == null)
+			{
+				this.icons = this.createIcons(this.currentState);
+
+				if (this.icons == null)
+				{
+					this.currentState.setCursor(mxConstants.CURSOR_CONNECT);
+					me.consume();
+				}
+			}
+
+			this.previous = this.currentState;
+		}
+		else if (this.previous == this.currentState && this.currentState != null && this.icons == null &&
+			!this.graph.isMouseDown)
+		{
+			// Makes sure that no cursors are changed
+			me.consume();
+		}
+
+		if (!this.graph.isMouseDown && this.currentState != null && this.icons != null)
+		{
+			var hitsIcon = false;
+			var target = me.getSource();
+			
+			for (var i = 0; i < this.icons.length && !hitsIcon; i++)
+			{
+				hitsIcon = target == this.icons[i].node || target.parentNode == this.icons[i].node;
+			}
+
+			if (!hitsIcon)
+			{
+				this.updateIcons(this.currentState, this.icons, me);
+			}
+		}
+	}
+	else
+	{
+		this.constraintHandler.reset();
+	}
+};
+
+/**
+ * Function: updateEdgeState
+ * 
+ * Updates <edgeState>.
+ */
+mxConnectionHandler.prototype.updateEdgeState = function(current, constraint)
+{
+	// TODO: Use generic method for writing constraint to style
+	if (this.sourceConstraint != null && this.sourceConstraint.point != null)
+	{
+		this.edgeState.style[mxConstants.STYLE_EXIT_X] = this.sourceConstraint.point.x;
+		this.edgeState.style[mxConstants.STYLE_EXIT_Y] = this.sourceConstraint.point.y;
+	}
+
+	if (constraint != null && constraint.point != null)
+	{
+		this.edgeState.style[mxConstants.STYLE_ENTRY_X] = constraint.point.x;
+		this.edgeState.style[mxConstants.STYLE_ENTRY_Y] = constraint.point.y;
+	}
+	else
+	{
+		delete this.edgeState.style[mxConstants.STYLE_ENTRY_X];
+		delete this.edgeState.style[mxConstants.STYLE_ENTRY_Y];
+	}
+	
+	this.edgeState.absolutePoints = [null, (this.currentState != null) ? null : current];
+	this.graph.view.updateFixedTerminalPoint(this.edgeState, this.previous, true, this.sourceConstraint);
+	
+	if (this.currentState != null)
+	{
+		if (constraint == null)
+		{
+			constraint = this.graph.getConnectionConstraint(this.edgeState, this.previous, false);
+		}
+		
+		this.edgeState.setAbsoluteTerminalPoint(null, false);
+		this.graph.view.updateFixedTerminalPoint(this.edgeState, this.currentState, false, constraint);
+	}
+	
+	// Scales and translates the waypoints to the model
+	var realPoints = null;
+	
+	if (this.waypoints != null)
+	{
+		realPoints = [];
+		
+		for (var i = 0; i < this.waypoints.length; i++)
+		{
+			var pt = this.waypoints[i].clone();
+			this.convertWaypoint(pt);
+			realPoints[i] = pt;
+		}
+	}
+	
+	this.graph.view.updatePoints(this.edgeState, realPoints, this.previous, this.currentState);
+	this.graph.view.updateFloatingTerminalPoints(this.edgeState, this.previous, this.currentState);
+};
+
+/**
+ * Function: getTargetPerimeterPoint
+ * 
+ * Returns the perimeter point for the given target state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents the target cell state.
+ * me - <mxMouseEvent> that represents the mouse move.
+ */
+mxConnectionHandler.prototype.getTargetPerimeterPoint = function(state, me)
+{
+	var result = null;
+	var view = state.view;
+	var targetPerimeter = view.getPerimeterFunction(state);
+	
+	if (targetPerimeter != null)
+	{
+		var next = (this.waypoints != null && this.waypoints.length > 0) ?
+				this.waypoints[this.waypoints.length - 1] :
+				new mxPoint(this.previous.getCenterX(), this.previous.getCenterY());
+		var tmp = targetPerimeter(view.getPerimeterBounds(state),
+			this.edgeState, next, false);
+			
+		if (tmp != null)
+		{
+			result = tmp;
+		}
+	}
+	else
+	{
+		result = new mxPoint(state.getCenterX(), state.getCenterY());
+	}
+	
+	return result;
+};
+
+/**
+ * Function: getSourcePerimeterPoint
+ * 
+ * Hook to update the icon position(s) based on a mouseOver event. This is
+ * an empty implementation.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> that represents the target cell state.
+ * next - <mxPoint> that represents the next point along the previewed edge.
+ * me - <mxMouseEvent> that represents the mouse move.
+ */
+mxConnectionHandler.prototype.getSourcePerimeterPoint = function(state, next, me)
+{
+	var result = null;
+	var view = state.view;
+	var sourcePerimeter = view.getPerimeterFunction(state);
+	var c = new mxPoint(state.getCenterX(), state.getCenterY());
+	
+	if (sourcePerimeter != null)
+	{
+		var theta = mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION, 0);
+		var rad = -theta * (Math.PI / 180);
+		
+		if (theta != 0)
+		{
+			next = mxUtils.getRotatedPoint(new mxPoint(next.x, next.y), Math.cos(rad), Math.sin(rad), c);
+		}
+		
+		var tmp = sourcePerimeter(view.getPerimeterBounds(state), state, next, false);
+			
+		if (tmp != null)
+		{
+			if (theta != 0)
+			{
+				tmp = mxUtils.getRotatedPoint(new mxPoint(tmp.x, tmp.y), Math.cos(-rad), Math.sin(-rad), c);
+			}
+			
+			result = tmp;
+		}
+	}
+	else
+	{
+		result = c;
+	}
+	
+	return result;
+};
+
+
+/**
+ * Function: updateIcons
+ * 
+ * Hook to update the icon position(s) based on a mouseOver event. This is
+ * an empty implementation.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> under the mouse.
+ * icons - Array of currently displayed icons.
+ * me - <mxMouseEvent> that contains the mouse event.
+ */
+mxConnectionHandler.prototype.updateIcons = function(state, icons, me)
+{
+	// empty
+};
+
+/**
+ * Function: isStopEvent
+ * 
+ * Returns true if the given mouse up event should stop this handler. The
+ * connection will be created if <error> is null. Note that this is only
+ * called if <waypointsEnabled> is true. This implemtation returns true
+ * if there is a cell state in the given event.
+ */
+mxConnectionHandler.prototype.isStopEvent = function(me)
+{
+	return me.getState() != null;
+};
+
+/**
+ * Function: addWaypoint
+ * 
+ * Adds the waypoint for the given event to <waypoints>.
+ */
+mxConnectionHandler.prototype.addWaypointForEvent = function(me)
+{
+	var point = mxUtils.convertPoint(this.graph.container, me.getX(), me.getY());
+	var dx = Math.abs(point.x - this.first.x);
+	var dy = Math.abs(point.y - this.first.y);
+	var addPoint = this.waypoints != null || (this.mouseDownCounter > 1 &&
+			(dx > this.graph.tolerance || dy > this.graph.tolerance));
+
+	if (addPoint)
+	{
+		if (this.waypoints == null)
+		{
+			this.waypoints = [];
+		}
+		
+		var scale = this.graph.view.scale;
+		var point = new mxPoint(this.graph.snap(me.getGraphX() / scale) * scale,
+				this.graph.snap(me.getGraphY() / scale) * scale);
+		this.waypoints.push(point);
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by inserting the new connection.
+ */
+mxConnectionHandler.prototype.mouseUp = function(sender, me)
+{
+	if (!me.isConsumed() && this.isConnecting())
+	{
+		if (this.waypointsEnabled && !this.isStopEvent(me))
+		{
+			this.addWaypointForEvent(me);
+			me.consume();
+			
+			return;
+		}
+		
+		// Inserts the edge if no validation error exists
+		if (this.error == null)
+		{
+			var source = (this.previous != null) ? this.previous.cell : null;
+			var target = null;
+			
+			if (this.constraintHandler.currentConstraint != null &&
+				this.constraintHandler.currentFocus != null)
+			{
+				target = this.constraintHandler.currentFocus.cell;
+			}
+			
+			if (target == null && this.currentState != null)
+			{
+				target = this.currentState.cell;
+			}
+			
+			this.connect(source, target, me.getEvent(), me.getCell());
+		}
+		else
+		{
+			// Selects the source terminal for self-references
+			if (this.previous != null && this.marker.validState != null &&
+				this.previous.cell == this.marker.validState.cell)
+			{
+				this.graph.selectCellForEvent(this.marker.source, evt);
+			}
+			
+			// Displays the error message if it is not an empty string,
+			// for empty error messages, the event is silently dropped
+			if (this.error.length > 0)
+			{
+				this.graph.validationAlert(this.error);
+			}
+		}
+		
+		// Redraws the connect icons and resets the handler state
+		this.destroyIcons();
+		me.consume();
+	}
+
+	if (this.first != null)
+	{
+		this.reset();
+	}
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of this handler.
+ */
+mxConnectionHandler.prototype.reset = function()
+{
+	if (this.shape != null)
+	{
+		this.shape.destroy();
+		this.shape = null;
+	}
+	
+	// Resets the cursor on the container
+	if (this.cursor != null && this.graph.container != null)
+	{
+		this.graph.container.style.cursor = '';
+	}
+	
+	this.destroyIcons();
+	this.marker.reset();
+	this.constraintHandler.reset();
+	this.originalPoint = null;
+	this.currentPoint = null;
+	this.edgeState = null;
+	this.previous = null;
+	this.error = null;
+	this.sourceConstraint = null;
+	this.mouseDownCounter = 0;
+	this.first = null;
+
+	this.fireEvent(new mxEventObject(mxEvent.RESET));
+};
+
+/**
+ * Function: drawPreview
+ * 
+ * Redraws the preview edge using the color and width returned by
+ * <getEdgeColor> and <getEdgeWidth>.
+ */
+mxConnectionHandler.prototype.drawPreview = function()
+{
+	this.updatePreview(this.error == null);
+	this.shape.redraw();
+};
+
+/**
+ * Function: getEdgeColor
+ * 
+ * Returns the color used to draw the preview edge. This returns green if
+ * there is no edge validation error and red otherwise.
+ * 
+ * Parameters:
+ * 
+ * valid - Boolean indicating if the color for a valid edge should be
+ * returned.
+ */
+mxConnectionHandler.prototype.updatePreview = function(valid)
+{
+	this.shape.strokewidth = this.getEdgeWidth(valid);
+	this.shape.stroke = this.getEdgeColor(valid);
+};
+
+/**
+ * Function: getEdgeColor
+ * 
+ * Returns the color used to draw the preview edge. This returns green if
+ * there is no edge validation error and red otherwise.
+ * 
+ * Parameters:
+ * 
+ * valid - Boolean indicating if the color for a valid edge should be
+ * returned.
+ */
+mxConnectionHandler.prototype.getEdgeColor = function(valid)
+{
+	return (valid) ? mxConstants.VALID_COLOR : mxConstants.INVALID_COLOR;
+};
+	
+/**
+ * Function: getEdgeWidth
+ * 
+ * Returns the width used to draw the preview edge. This returns 3 if
+ * there is no edge validation error and 1 otherwise.
+ * 
+ * Parameters:
+ * 
+ * valid - Boolean indicating if the width for a valid edge should be
+ * returned.
+ */
+mxConnectionHandler.prototype.getEdgeWidth = function(valid)
+{
+	return (valid) ? 3 : 1;
+};
+
+/**
+ * Function: connect
+ * 
+ * Connects the given source and target using a new edge. This
+ * implementation uses <createEdge> to create the edge.
+ * 
+ * Parameters:
+ * 
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ * evt - Mousedown event of the connect gesture.
+ * dropTarget - <mxCell> that represents the cell under the mouse when it was
+ * released.
+ */
+mxConnectionHandler.prototype.connect = function(source, target, evt, dropTarget)
+{
+	if (target != null || this.isCreateTarget(evt) || this.graph.allowDanglingEdges)
+	{
+		// Uses the common parent of source and target or
+		// the default parent to insert the edge
+		var model = this.graph.getModel();
+		var terminalInserted = false;
+		var edge = null;
+
+		model.beginUpdate();
+		try
+		{
+			if (source != null && target == null && !this.graph.isIgnoreTerminalEvent(evt) && this.isCreateTarget(evt))
+			{
+				target = this.createTargetVertex(evt, source);
+				
+				if (target != null)
+				{
+					dropTarget = this.graph.getDropTarget([target], evt, dropTarget);
+					terminalInserted = true;
+					
+					// Disables edges as drop targets if the target cell was created
+					// FIXME: Should not shift if vertex was aligned (same in Java)
+					if (dropTarget == null || !this.graph.getModel().isEdge(dropTarget))
+					{
+						var pstate = this.graph.getView().getState(dropTarget);
+						
+						if (pstate != null)
+						{
+							var tmp = model.getGeometry(target);
+							tmp.x -= pstate.origin.x;
+							tmp.y -= pstate.origin.y;
+						}
+					}
+					else
+					{
+						dropTarget = this.graph.getDefaultParent();
+					}
+						
+					this.graph.addCell(target, dropTarget);
+				}
+			}
+
+			var parent = this.graph.getDefaultParent();
+
+			if (source != null && target != null &&
+				model.getParent(source) == model.getParent(target) &&
+				model.getParent(model.getParent(source)) != model.getRoot())
+			{
+				parent = model.getParent(source);
+
+				if ((source.geometry != null && source.geometry.relative) &&
+					(target.geometry != null && target.geometry.relative))
+				{
+					parent = model.getParent(parent);
+				}
+			}
+			
+			// Uses the value of the preview edge state for inserting
+			// the new edge into the graph
+			var value = null;
+			var style = null;
+			
+			if (this.edgeState != null)
+			{
+				value = this.edgeState.cell.value;
+				style = this.edgeState.cell.style;
+			}
+
+			edge = this.insertEdge(parent, null, value, source, target, style);
+			
+			if (edge != null)
+			{
+				// Updates the connection constraints
+				this.graph.setConnectionConstraint(edge, source, true, this.sourceConstraint);
+				this.graph.setConnectionConstraint(edge, target, false, this.constraintHandler.currentConstraint);
+				
+				// Uses geometry of the preview edge state
+				if (this.edgeState != null)
+				{
+					model.setGeometry(edge, this.edgeState.cell.geometry);
+				}
+				
+				var parent = model.getParent(source);
+				
+				// Inserts edge before source
+				if (this.isInsertBefore(edge, source, target, evt, dropTarget))
+				{
+					var index = null;
+					var tmp = source;
+
+					while (tmp.parent != null && tmp.geometry != null &&
+						tmp.geometry.relative && tmp.parent != edge.parent)
+					{
+						tmp = this.graph.model.getParent(tmp);
+					}
+
+					if (tmp != null && tmp.parent != null && tmp.parent == edge.parent)
+					{
+						var index = tmp.parent.getIndex(tmp);
+						tmp.parent.insert(edge, index);
+					}
+				}
+				
+				// Makes sure the edge has a non-null, relative geometry
+				var geo = model.getGeometry(edge);
+
+				if (geo == null)
+				{
+					geo = new mxGeometry();
+					geo.relative = true;
+					
+					model.setGeometry(edge, geo);
+				}
+				
+				// Uses scaled waypoints in geometry
+				if (this.waypoints != null && this.waypoints.length > 0)
+				{
+					var s = this.graph.view.scale;
+					var tr = this.graph.view.translate;
+					geo.points = [];
+					
+					for (var i = 0; i < this.waypoints.length; i++)
+					{
+						var pt = this.waypoints[i];
+						geo.points.push(new mxPoint(pt.x / s - tr.x, pt.y / s - tr.y));
+					}
+				}
+
+				if (target == null)
+				{
+					var t = this.graph.view.translate;
+					var s = this.graph.view.scale;
+					var pt = (this.originalPoint != null) ?
+							new mxPoint(this.originalPoint.x / s - t.x, this.originalPoint.y / s - t.y) :
+						new mxPoint(this.currentPoint.x / s - t.x, this.currentPoint.y / s - t.y);
+					pt.x -= this.graph.panDx / this.graph.view.scale;
+					pt.y -= this.graph.panDy / this.graph.view.scale;
+					geo.setTerminalPoint(pt, false);
+				}
+				
+				this.fireEvent(new mxEventObject(mxEvent.CONNECT, 'cell', edge, 'terminal', target,
+					'event', evt, 'target', dropTarget, 'terminalInserted', terminalInserted));
+			}
+		}
+		catch (e)
+		{
+			mxLog.show();
+			mxLog.debug(e.message);
+		}
+		finally
+		{
+			model.endUpdate();
+		}
+		
+		if (this.select)
+		{
+			this.selectCells(edge, (terminalInserted) ? target : null);
+		}
+	}
+};
+
+/**
+ * Function: selectCells
+ * 
+ * Selects the given edge after adding a new connection. The target argument
+ * contains the target vertex if one has been inserted.
+ */
+mxConnectionHandler.prototype.selectCells = function(edge, target)
+{
+	this.graph.setSelectionCell(edge);
+};
+
+/**
+ * Function: insertEdge
+ * 
+ * Creates, inserts and returns the new edge for the given parameters. This
+ * implementation does only use <createEdge> if <factoryMethod> is defined,
+ * otherwise <mxGraph.insertEdge> will be used.
+ */
+mxConnectionHandler.prototype.insertEdge = function(parent, id, value, source, target, style)
+{
+	if (this.factoryMethod == null)
+	{
+		return this.graph.insertEdge(parent, id, value, source, target, style);
+	}
+	else
+	{
+		var edge = this.createEdge(value, source, target, style);
+		edge = this.graph.addEdge(edge, parent, source, target);
+		
+		return edge;
+	}
+};
+
+/**
+ * Function: createTargetVertex
+ * 
+ * Hook method for creating new vertices on the fly if no target was
+ * under the mouse. This is only called if <createTarget> is true and
+ * returns null.
+ * 
+ * Parameters:
+ * 
+ * evt - Mousedown event of the connect gesture.
+ * source - <mxCell> that represents the source terminal.
+ */
+mxConnectionHandler.prototype.createTargetVertex = function(evt, source)
+{
+	// Uses the first non-relative source
+	var geo = this.graph.getCellGeometry(source);
+	
+	while (geo != null && geo.relative)
+	{
+		source = this.graph.getModel().getParent(source);
+		geo = this.graph.getCellGeometry(source);
+	}
+	
+	var clone = this.graph.cloneCells([source])[0];
+	var geo = this.graph.getModel().getGeometry(clone);
+	
+	if (geo != null)
+	{
+		var t = this.graph.view.translate;
+		var s = this.graph.view.scale;
+		var point = new mxPoint(this.currentPoint.x / s - t.x, this.currentPoint.y / s - t.y);
+		geo.x = Math.round(point.x - geo.width / 2 - this.graph.panDx / s);
+		geo.y = Math.round(point.y - geo.height / 2 - this.graph.panDy / s);
+
+		// Aligns with source if within certain tolerance
+		var tol = this.getAlignmentTolerance();
+		
+		if (tol > 0)
+		{
+			var sourceState = this.graph.view.getState(source);
+			
+			if (sourceState != null)
+			{
+				var x = sourceState.x / s - t.x;
+				var y = sourceState.y / s - t.y;
+				
+				if (Math.abs(x - geo.x) <= tol)
+				{
+					geo.x = Math.round(x);
+				}
+				
+				if (Math.abs(y - geo.y) <= tol)
+				{
+					geo.y = Math.round(y);
+				}
+			}
+		}
+	}
+
+	return clone;		
+};
+
+/**
+ * Function: getAlignmentTolerance
+ * 
+ * Returns the tolerance for aligning new targets to sources. This returns the grid size / 2.
+ */
+mxConnectionHandler.prototype.getAlignmentTolerance = function(evt)
+{
+	return (this.graph.isGridEnabled()) ? this.graph.gridSize / 2 : this.graph.tolerance;
+};
+
+/**
+ * Function: createEdge
+ * 
+ * Creates and returns a new edge using <factoryMethod> if one exists. If
+ * no factory method is defined, then a new default edge is returned. The
+ * source and target arguments are informal, the actual connection is
+ * setup later by the caller of this function.
+ * 
+ * Parameters:
+ * 
+ * value - Value to be used for creating the edge.
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ * style - Optional style from the preview edge.
+ */
+mxConnectionHandler.prototype.createEdge = function(value, source, target, style)
+{
+	var edge = null;
+	
+	// Creates a new edge using the factoryMethod
+	if (this.factoryMethod != null)
+	{
+		edge = this.factoryMethod(source, target, style);
+	}
+	
+	if (edge == null)
+	{
+		edge = new mxCell(value || '');
+		edge.setEdge(true);
+		edge.setStyle(style);
+		
+		var geo = new mxGeometry();
+		geo.relative = true;
+		edge.setGeometry(geo);
+	}
+
+	return edge;
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes. This should be
+ * called on all instances. It is called automatically for the built-in
+ * instance created for each <mxGraph>.
+ */
+mxConnectionHandler.prototype.destroy = function()
+{
+	this.graph.removeMouseListener(this);
+	
+	if (this.shape != null)
+	{
+		this.shape.destroy();
+		this.shape = null;
+	}
+	
+	if (this.marker != null)
+	{
+		this.marker.destroy();
+		this.marker = null;
+	}
+
+	if (this.constraintHandler != null)
+	{
+		this.constraintHandler.destroy();
+		this.constraintHandler = null;
+	}
+
+	if (this.changeHandler != null)
+	{
+		this.graph.getModel().removeListener(this.changeHandler);
+		this.graph.getView().removeListener(this.changeHandler);
+		this.changeHandler = null;
+	}
+	
+	if (this.drillHandler != null)
+	{
+		this.graph.removeListener(this.drillHandler);
+		this.graph.getView().removeListener(this.drillHandler);
+		this.drillHandler = null;
+	}
+	
+	if (this.escapeHandler != null)
+	{
+		this.graph.removeListener(this.escapeHandler);
+		this.escapeHandler = null;
+	}
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/handler/mxConstraintHandler.js b/airavata-kubernetes/web-console/src/assets/js/handler/mxConstraintHandler.js
new file mode 100644
index 0000000..2ea07a2
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/handler/mxConstraintHandler.js
@@ -0,0 +1,520 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxConstraintHandler
+ *
+ * Handles constraints on connection targets. This class is in charge of
+ * showing fixed points when the mouse is over a vertex and handles constraints
+ * to establish new connections.
+ *
+ * Constructor: mxConstraintHandler
+ *
+ * Constructs an new constraint handler.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * factoryMethod - Optional function to create the edge. The function takes
+ * the source and target <mxCell> as the first and second argument and
+ * returns the <mxCell> that represents the new edge.
+ */
+function mxConstraintHandler(graph)
+{
+	this.graph = graph;
+	
+	// Adds a graph model listener to update the current focus on changes
+	this.resetHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		if (this.currentFocus != null && this.graph.view.getState(this.currentFocus.cell) == null)
+		{
+			this.reset();
+		}
+		else
+		{
+			this.redraw();
+		}
+	});
+	
+	this.graph.model.addListener(mxEvent.CHANGE, this.resetHandler);
+	this.graph.view.addListener(mxEvent.SCALE, this.resetHandler);
+	this.graph.addListener(mxEvent.ROOT, this.resetHandler);
+};
+
+/**
+ * Variable: pointImage
+ * 
+ * <mxImage> to be used as the image for fixed connection points.
+ */
+mxConstraintHandler.prototype.pointImage = new mxImage(mxClient.imageBasePath + '/point.gif', 5, 5);
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxConstraintHandler.prototype.graph = null;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxConstraintHandler.prototype.enabled = true;
+
+/**
+ * Variable: highlightColor
+ * 
+ * Specifies the color for the highlight. Default is <mxConstants.DEFAULT_VALID_COLOR>.
+ */
+mxConstraintHandler.prototype.highlightColor = mxConstants.DEFAULT_VALID_COLOR;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxConstraintHandler.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+	
+/**
+ * Function: setEnabled
+ * 
+ * Enables or disables event handling. This implementation
+ * updates <enabled>.
+ * 
+ * Parameters:
+ * 
+ * enabled - Boolean that specifies the new enabled state.
+ */
+mxConstraintHandler.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of this handler.
+ */
+mxConstraintHandler.prototype.reset = function()
+{
+	if (this.focusIcons != null)
+	{
+		for (var i = 0; i < this.focusIcons.length; i++)
+		{
+			this.focusIcons[i].destroy();
+		}
+		
+		this.focusIcons = null;
+	}
+	
+	if (this.focusHighlight != null)
+	{
+		this.focusHighlight.destroy();
+		this.focusHighlight = null;
+	}
+	
+	this.currentConstraint = null;
+	this.currentFocusArea = null;
+	this.currentPoint = null;
+	this.currentFocus = null;
+	this.focusPoints = null;
+};
+
+/**
+ * Function: getTolerance
+ * 
+ * Returns the tolerance to be used for intersecting connection points. This
+ * implementation returns <mxGraph.tolerance>.
+ * 
+ * Parameters:
+ * 
+ * me - <mxMouseEvent> whose tolerance should be returned.
+ */
+mxConstraintHandler.prototype.getTolerance = function(me)
+{
+	return this.graph.getTolerance();
+};
+
+/**
+ * Function: getImageForConstraint
+ * 
+ * Returns the tolerance to be used for intersecting connection points.
+ */
+mxConstraintHandler.prototype.getImageForConstraint = function(state, constraint, point)
+{
+	return this.pointImage;
+};
+
+/**
+ * Function: isEventIgnored
+ * 
+ * Returns true if the given <mxMouseEvent> should be ignored in <update>. This
+ * implementation always returns false.
+ */
+mxConstraintHandler.prototype.isEventIgnored = function(me, source)
+{
+	return false;
+};
+
+/**
+ * Function: isStateIgnored
+ * 
+ * Returns true if the given state should be ignored. This always returns false.
+ */
+mxConstraintHandler.prototype.isStateIgnored = function(state, source)
+{
+	return false;
+};
+
+/**
+ * Function: destroyIcons
+ * 
+ * Destroys the <focusIcons> if they exist.
+ */
+mxConstraintHandler.prototype.destroyIcons = function()
+{
+	if (this.focusIcons != null)
+	{
+		for (var i = 0; i < this.focusIcons.length; i++)
+		{
+			this.focusIcons[i].destroy();
+		}
+		
+		this.focusIcons = null;
+		this.focusPoints = null;
+	}
+};
+
+/**
+ * Function: destroyFocusHighlight
+ * 
+ * Destroys the <focusHighlight> if one exists.
+ */
+mxConstraintHandler.prototype.destroyFocusHighlight = function()
+{
+	if (this.focusHighlight != null)
+	{
+		this.focusHighlight.destroy();
+		this.focusHighlight = null;
+	}
+};
+
+/**
+ * Function: isKeepFocusEvent
+ * 
+ * Returns true if the current focused state should not be changed for the given event.
+ * This returns true if shift and alt are pressed.
+ */
+mxConstraintHandler.prototype.isKeepFocusEvent = function(me)
+{
+	return mxEvent.isShiftDown(me.getEvent());
+};
+
+/**
+ * Function: getCellForEvent
+ * 
+ * Returns the cell for the given event.
+ */
+mxConstraintHandler.prototype.getCellForEvent = function(me, point)
+{
+	var cell = me.getCell();
+	
+	// Gets cell under actual point if different from event location
+	if (cell == null && point != null && (me.getGraphX() != point.x || me.getGraphY() != point.y))
+	{
+		cell = this.graph.getCellAt(point.x, point.y);
+	}
+	
+	// Uses connectable parent vertex if one exists
+	if (cell != null && !this.graph.isCellConnectable(cell))
+	{
+		var parent = this.graph.getModel().getParent(cell);
+		
+		if (this.graph.getModel().isVertex(parent) && this.graph.isCellConnectable(parent))
+		{
+			cell = parent;
+		}
+	}
+	
+	return cell;
+};
+
+/**
+ * Function: update
+ * 
+ * Updates the state of this handler based on the given <mxMouseEvent>.
+ * Source is a boolean indicating if the cell is a source or target.
+ */
+mxConstraintHandler.prototype.update = function(me, source, existingEdge, point)
+{
+	if (this.isEnabled() && !this.isEventIgnored(me))
+	{
+		// Lazy installation of mouseleave handler
+		if (this.mouseleaveHandler == null && this.graph.container != null)
+		{
+			this.mouseleaveHandler = mxUtils.bind(this, function()
+			{
+				this.reset();
+			});
+
+			mxEvent.addListener(this.graph.container, 'mouseleave', this.resetHandler);	
+		}
+		
+		var tol = this.getTolerance(me);
+		var x = (point != null) ? point.x : me.getGraphX();
+		var y = (point != null) ? point.y : me.getGraphY();
+		var grid = new mxRectangle(x - tol, y - tol, 2 * tol, 2 * tol);
+		var mouse = new mxRectangle(me.getGraphX() - tol, me.getGraphY() - tol, 2 * tol, 2 * tol);
+		var state = this.graph.view.getState(this.getCellForEvent(me, point));
+
+		// Keeps focus icons visible while over vertex bounds and no other cell under mouse or shift is pressed
+		if (!this.isKeepFocusEvent(me) && (this.currentFocusArea == null || this.currentFocus == null ||
+			(state != null) || !this.graph.getModel().isVertex(this.currentFocus.cell) ||
+			!mxUtils.intersects(this.currentFocusArea, mouse)) && (state != this.currentFocus))
+		{
+			this.currentFocusArea = null;
+			this.currentFocus = null;
+			this.setFocus(me, state, source);
+		}
+
+		this.currentConstraint = null;
+		this.currentPoint = null;
+		var minDistSq = null;
+		
+		if (this.focusIcons != null && this.constraints != null &&
+			(state == null || this.currentFocus == state))
+		{
+			var cx = mouse.getCenterX();
+			var cy = mouse.getCenterY();
+			
+			for (var i = 0; i < this.focusIcons.length; i++)
+			{
+				var dx = cx - this.focusIcons[i].bounds.getCenterX();
+				var dy = cy - this.focusIcons[i].bounds.getCenterY();
+				var tmp = dx * dx + dy * dy;
+				
+				if ((this.intersects(this.focusIcons[i], mouse, source, existingEdge) || (point != null &&
+					this.intersects(this.focusIcons[i], grid, source, existingEdge))) &&
+					(minDistSq == null || tmp < minDistSq))
+				{
+					this.currentConstraint = this.constraints[i];
+					this.currentPoint = this.focusPoints[i];
+					minDistSq = tmp;
+					
+					var tmp = this.focusIcons[i].bounds.clone();
+					tmp.grow(mxConstants.HIGHLIGHT_SIZE);
+					
+					if (mxClient.IS_IE)
+					{
+						tmp.grow(1);
+						tmp.width -= 1;
+						tmp.height -= 1;
+					}
+					
+					if (this.focusHighlight == null)
+					{
+						var hl = this.createHighlightShape();
+						hl.dialect = (this.graph.dialect == mxConstants.DIALECT_SVG) ?
+								mxConstants.DIALECT_SVG : mxConstants.DIALECT_VML;
+						hl.pointerEvents = false;
+
+						hl.init(this.graph.getView().getOverlayPane());
+						this.focusHighlight = hl;
+						
+						var getState = mxUtils.bind(this, function()
+						{
+							return (this.currentFocus != null) ? this.currentFocus : state;
+						});
+	
+						mxEvent.redirectMouseEvents(hl.node, this.graph, getState);
+					}
+
+					this.focusHighlight.bounds = tmp;
+					this.focusHighlight.redraw();
+				}
+			}
+		}
+		
+		if (this.currentConstraint == null)
+		{
+			this.destroyFocusHighlight();
+		}
+	}
+	else
+	{
+		this.currentConstraint = null;
+		this.currentFocus = null;
+		this.currentPoint = null;
+	}
+};
+
+/**
+ * Function: redraw
+ * 
+ * Transfers the focus to the given state as a source or target terminal. If
+ * the handler is not enabled then the outline is painted, but the constraints
+ * are ignored.
+ */
+mxConstraintHandler.prototype.redraw = function()
+{
+	if (this.currentFocus != null && this.constraints != null && this.focusIcons != null)
+	{
+		var state = this.graph.view.getState(this.currentFocus.cell);
+		this.currentFocus = state;
+		this.currentFocusArea = new mxRectangle(state.x, state.y, state.width, state.height);
+		
+		for (var i = 0; i < this.constraints.length; i++)
+		{
+			var cp = this.graph.getConnectionPoint(state, this.constraints[i]);
+			var img = this.getImageForConstraint(state, this.constraints[i], cp);
+
+			var bounds = new mxRectangle(Math.round(cp.x - img.width / 2),
+				Math.round(cp.y - img.height / 2), img.width, img.height);
+			this.focusIcons[i].bounds = bounds;
+			this.focusIcons[i].redraw();
+			this.currentFocusArea.add(this.focusIcons[i].bounds);
+			this.focusPoints[i] = cp;
+		}
+	}	
+};
+
+/**
+ * Function: setFocus
+ * 
+ * Transfers the focus to the given state as a source or target terminal. If
+ * the handler is not enabled then the outline is painted, but the constraints
+ * are ignored.
+ */
+mxConstraintHandler.prototype.setFocus = function(me, state, source)
+{
+	this.constraints = (state != null && !this.isStateIgnored(state, source) &&
+		this.graph.isCellConnectable(state.cell)) ? ((this.isEnabled()) ?
+		(this.graph.getAllConnectionConstraints(state, source) || []) : []) : null;
+
+	// Only uses cells which have constraints
+	if (this.constraints != null)
+	{
+		this.currentFocus = state;
+		this.currentFocusArea = new mxRectangle(state.x, state.y, state.width, state.height);
+		
+		if (this.focusIcons != null)
+		{
+			for (var i = 0; i < this.focusIcons.length; i++)
+			{
+				this.focusIcons[i].destroy();
+			}
+			
+			this.focusIcons = null;
+			this.focusPoints = null;
+		}
+		
+		this.focusPoints = [];
+		this.focusIcons = [];
+		
+		for (var i = 0; i < this.constraints.length; i++)
+		{
+			var cp = this.graph.getConnectionPoint(state, this.constraints[i]);
+			var img = this.getImageForConstraint(state, this.constraints[i], cp);
+
+			var src = img.src;
+			var bounds = new mxRectangle(Math.round(cp.x - img.width / 2),
+				Math.round(cp.y - img.height / 2), img.width, img.height);
+			var icon = new mxImageShape(bounds, src);
+			icon.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+					mxConstants.DIALECT_MIXEDHTML : mxConstants.DIALECT_SVG;
+			icon.preserveImageAspect = false;
+			icon.init(this.graph.getView().getDecoratorPane());
+			
+			// Fixes lost event tracking for images in quirks / IE8 standards
+			if (mxClient.IS_QUIRKS || document.documentMode == 8)
+			{
+				mxEvent.addListener(icon.node, 'dragstart', function(evt)
+				{
+					mxEvent.consume(evt);
+					
+					return false;
+				});
+			}
+			
+			// Move the icon behind all other overlays
+			if (icon.node.previousSibling != null)
+			{
+				icon.node.parentNode.insertBefore(icon.node, icon.node.parentNode.firstChild);
+			}
+
+			var getState = mxUtils.bind(this, function()
+			{
+				return (this.currentFocus != null) ? this.currentFocus : state;
+			});
+			
+			icon.redraw();
+
+			mxEvent.redirectMouseEvents(icon.node, this.graph, getState);
+			this.currentFocusArea.add(icon.bounds);
+			this.focusIcons.push(icon);
+			this.focusPoints.push(cp);
+		}
+		
+		this.currentFocusArea.grow(this.getTolerance(me));
+	}
+	else
+	{
+		this.destroyIcons();
+		this.destroyFocusHighlight();
+	}
+};
+
+/**
+ * Function: createHighlightShape
+ * 
+ * Create the shape used to paint the highlight.
+ * 
+ * Returns true if the given icon intersects the given point.
+ */
+mxConstraintHandler.prototype.createHighlightShape = function()
+{
+	var hl = new mxRectangleShape(null, this.highlightColor, this.highlightColor, mxConstants.HIGHLIGHT_STROKEWIDTH);
+	hl.opacity = mxConstants.HIGHLIGHT_OPACITY;
+	
+	return hl;
+};
+
+/**
+ * Function: intersects
+ * 
+ * Returns true if the given icon intersects the given rectangle.
+ */
+mxConstraintHandler.prototype.intersects = function(icon, mouse, source, existingEdge)
+{
+	return mxUtils.intersects(icon.bounds, mouse);
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroy this handler.
+ */
+mxConstraintHandler.prototype.destroy = function()
+{
+	this.reset();
+	
+	if (this.resetHandler != null)
+	{
+		this.graph.model.removeListener(this.resetHandler);
+		this.graph.view.removeListener(this.resetHandler);
+		this.graph.removeListener(this.resetHandler);
+		this.resetHandler = null;
+	}
+	
+	if (this.mouseleaveHandler != null && this.graph.container != null)
+	{
+		mxEvent.removeListener(this.graph.container, 'mouseleave', this.mouseleaveHandler);
+		this.mouseleaveHandler = null;
+	}
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/handler/mxEdgeHandler.js b/airavata-kubernetes/web-console/src/assets/js/handler/mxEdgeHandler.js
new file mode 100644
index 0000000..8dd7761
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/handler/mxEdgeHandler.js
@@ -0,0 +1,2409 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxEdgeHandler
+ *
+ * Graph event handler that reconnects edges and modifies control points and
+ * the edge label location. Uses <mxTerminalMarker> for finding and
+ * highlighting new source and target vertices. This handler is automatically
+ * created in <mxGraph.createHandler> for each selected edge.
+ * 
+ * To enable adding/removing control points, the following code can be used:
+ * 
+ * (code)
+ * mxEdgeHandler.prototype.addEnabled = true;
+ * mxEdgeHandler.prototype.removeEnabled = true;
+ * (end)
+ * 
+ * Note: This experimental feature is not recommended for production use.
+ * 
+ * Constructor: mxEdgeHandler
+ *
+ * Constructs an edge handler for the specified <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> of the cell to be handled.
+ */
+function mxEdgeHandler(state)
+{
+	if (state != null)
+	{
+		this.state = state;
+		this.init();
+		
+		// Handles escape keystrokes
+		this.escapeHandler = mxUtils.bind(this, function(sender, evt)
+		{
+			this.reset();
+		});
+		
+		this.state.view.graph.addListener(mxEvent.ESCAPE, this.escapeHandler);
+	}
+};
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxEdgeHandler.prototype.graph = null;
+
+/**
+ * Variable: state
+ * 
+ * Reference to the <mxCellState> being modified.
+ */
+mxEdgeHandler.prototype.state = null;
+
+/**
+ * Variable: marker
+ * 
+ * Holds the <mxTerminalMarker> which is used for highlighting terminals.
+ */
+mxEdgeHandler.prototype.marker = null;
+
+/**
+ * Variable: constraintHandler
+ * 
+ * Holds the <mxConstraintHandler> used for drawing and highlighting
+ * constraints.
+ */
+mxEdgeHandler.prototype.constraintHandler = null;
+
+/**
+ * Variable: error
+ * 
+ * Holds the current validation error while a connection is being changed.
+ */
+mxEdgeHandler.prototype.error = null;
+
+/**
+ * Variable: shape
+ * 
+ * Holds the <mxShape> that represents the preview edge.
+ */
+mxEdgeHandler.prototype.shape = null;
+
+/**
+ * Variable: bends
+ * 
+ * Holds the <mxShapes> that represent the points.
+ */
+mxEdgeHandler.prototype.bends = null;
+
+/**
+ * Variable: labelShape
+ * 
+ * Holds the <mxShape> that represents the label position.
+ */
+mxEdgeHandler.prototype.labelShape = null;
+
+/**
+ * Variable: cloneEnabled
+ * 
+ * Specifies if cloning by control-drag is enabled. Default is true.
+ */
+mxEdgeHandler.prototype.cloneEnabled = true;
+
+/**
+ * Variable: addEnabled
+ * 
+ * Specifies if adding bends by shift-click is enabled. Default is false.
+ * Note: This experimental feature is not recommended for production use.
+ */
+mxEdgeHandler.prototype.addEnabled = false;
+
+/**
+ * Variable: removeEnabled
+ * 
+ * Specifies if removing bends by shift-click is enabled. Default is false.
+ * Note: This experimental feature is not recommended for production use.
+ */
+mxEdgeHandler.prototype.removeEnabled = false;
+
+/**
+ * Variable: dblClickRemoveEnabled
+ * 
+ * Specifies if removing bends by double click is enabled. Default is false.
+ */
+mxEdgeHandler.prototype.dblClickRemoveEnabled = false;
+
+/**
+ * Variable: mergeRemoveEnabled
+ * 
+ * Specifies if removing bends by dropping them on other bends is enabled.
+ * Default is false.
+ */
+mxEdgeHandler.prototype.mergeRemoveEnabled = false;
+
+/**
+ * Variable: straightRemoveEnabled
+ * 
+ * Specifies if removing bends by creating straight segments should be enabled.
+ * If enabled, this can be overridden by holding down the alt key while moving.
+ * Default is false.
+ */
+mxEdgeHandler.prototype.straightRemoveEnabled = false;
+
+/**
+ * Variable: virtualBendsEnabled
+ * 
+ * Specifies if virtual bends should be added in the center of each
+ * segments. These bends can then be used to add new waypoints.
+ * Default is false.
+ */
+mxEdgeHandler.prototype.virtualBendsEnabled = false;
+
+/**
+ * Variable: virtualBendOpacity
+ * 
+ * Opacity to be used for virtual bends (see <virtualBendsEnabled>).
+ * Default is 20.
+ */
+mxEdgeHandler.prototype.virtualBendOpacity = 20;
+
+/**
+ * Variable: parentHighlightEnabled
+ * 
+ * Specifies if the parent should be highlighted if a child cell is selected.
+ * Default is false.
+ */
+mxEdgeHandler.prototype.parentHighlightEnabled = false;
+
+/**
+ * Variable: preferHtml
+ * 
+ * Specifies if bends should be added to the graph container. This is updated
+ * in <init> based on whether the edge or one of its terminals has an HTML
+ * label in the container.
+ */
+mxEdgeHandler.prototype.preferHtml = false;
+
+/**
+ * Variable: allowHandleBoundsCheck
+ * 
+ * Specifies if the bounds of handles should be used for hit-detection in IE
+ * Default is true.
+ */
+mxEdgeHandler.prototype.allowHandleBoundsCheck = true;
+
+/**
+ * Variable: snapToTerminals
+ * 
+ * Specifies if waypoints should snap to the routing centers of terminals.
+ * Default is false.
+ */
+mxEdgeHandler.prototype.snapToTerminals = false;
+
+/**
+ * Variable: handleImage
+ * 
+ * Optional <mxImage> to be used as handles. Default is null.
+ */
+mxEdgeHandler.prototype.handleImage = null;
+
+/**
+ * Variable: tolerance
+ * 
+ * Optional tolerance for hit-detection in <getHandleForEvent>. Default is 0.
+ */
+mxEdgeHandler.prototype.tolerance = 0;
+
+/**
+ * Variable: outlineConnect
+ * 
+ * Specifies if connections to the outline of a highlighted target should be
+ * enabled. This will allow to place the connection point along the outline of
+ * the highlighted target. Default is false.
+ */
+mxEdgeHandler.prototype.outlineConnect = false;
+
+/**
+ * Variable: manageLabelHandle
+ * 
+ * Specifies if the label handle should be moved if it intersects with another
+ * handle. Uses <checkLabelHandle> for checking and moving. Default is false.
+ */
+mxEdgeHandler.prototype.manageLabelHandle = false;
+
+/**
+ * Function: init
+ * 
+ * Initializes the shapes required for this edge handler.
+ */
+mxEdgeHandler.prototype.init = function()
+{
+	this.graph = this.state.view.graph;
+	this.marker = this.createMarker();
+	this.constraintHandler = new mxConstraintHandler(this.graph);
+	
+	// Clones the original points from the cell
+	// and makes sure at least one point exists
+	this.points = [];
+	
+	// Uses the absolute points of the state
+	// for the initial configuration and preview
+	this.abspoints = this.getSelectionPoints(this.state);
+	this.shape = this.createSelectionShape(this.abspoints);
+	this.shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+		mxConstants.DIALECT_MIXEDHTML : mxConstants.DIALECT_SVG;
+	this.shape.init(this.graph.getView().getOverlayPane());
+	this.shape.pointerEvents = false;
+	this.shape.setCursor(mxConstants.CURSOR_MOVABLE_EDGE);
+	mxEvent.redirectMouseEvents(this.shape.node, this.graph, this.state);
+
+	// Updates preferHtml
+	this.preferHtml = this.state.text != null &&
+		this.state.text.node.parentNode == this.graph.container;
+	
+	if (!this.preferHtml)
+	{
+		// Checks source terminal
+		var sourceState = this.state.getVisibleTerminalState(true);
+		
+		if (sourceState != null)
+		{
+			this.preferHtml = sourceState.text != null &&
+				sourceState.text.node.parentNode == this.graph.container;
+		}
+		
+		if (!this.preferHtml)
+		{
+			// Checks target terminal
+			var targetState = this.state.getVisibleTerminalState(false);
+			
+			if (targetState != null)
+			{
+				this.preferHtml = targetState.text != null &&
+				targetState.text.node.parentNode == this.graph.container;
+			}
+		}
+	}
+	
+	// Adds highlight for parent group
+	if (this.parentHighlightEnabled)
+	{
+		var parent = this.graph.model.getParent(this.state.cell);
+		
+		if (this.graph.model.isVertex(parent))
+		{
+			var pstate = this.graph.view.getState(parent);
+			
+			if (pstate != null)
+			{
+				this.parentHighlight = this.createParentHighlightShape(pstate);
+				// VML dialect required here for event transparency in IE
+				this.parentHighlight.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+				this.parentHighlight.pointerEvents = false;
+				this.parentHighlight.rotation = Number(pstate.style[mxConstants.STYLE_ROTATION] || '0');
+				this.parentHighlight.init(this.graph.getView().getOverlayPane());
+			}
+		}
+	}
+	
+	// Creates bends for the non-routed absolute points
+	// or bends that don't correspond to points
+	if (this.graph.getSelectionCount() < mxGraphHandler.prototype.maxCells ||
+		mxGraphHandler.prototype.maxCells <= 0)
+	{
+		this.bends = this.createBends();
+
+		if (this.isVirtualBendsEnabled())
+		{
+			this.virtualBends = this.createVirtualBends();
+		}
+	}
+
+	// Adds a rectangular handle for the label position
+	this.label = new mxPoint(this.state.absoluteOffset.x, this.state.absoluteOffset.y);
+	this.labelShape = this.createLabelHandleShape();
+	this.initBend(this.labelShape);
+	this.labelShape.setCursor(mxConstants.CURSOR_LABEL_HANDLE);
+	
+	this.customHandles = this.createCustomHandles();
+	
+	this.redraw();
+};
+
+/**
+ * Function: createCustomHandles
+ * 
+ * Returns an array of custom handles. This implementation returns null.
+ */
+mxEdgeHandler.prototype.createCustomHandles = function()
+{
+	return null;
+};
+
+/**
+ * Function: isVirtualBendsEnabled
+ * 
+ * Returns true if virtual bends should be added. This returns true if
+ * <virtualBendsEnabled> is true and the current style allows and
+ * renders custom waypoints.
+ */
+mxEdgeHandler.prototype.isVirtualBendsEnabled = function(evt)
+{
+	return this.virtualBendsEnabled && (this.state.style[mxConstants.STYLE_EDGE] == null ||
+			this.state.style[mxConstants.STYLE_EDGE] == mxConstants.NONE ||
+			this.state.style[mxConstants.STYLE_NOEDGESTYLE] == 1)  &&
+			mxUtils.getValue(this.state.style, mxConstants.STYLE_SHAPE, null) != 'arrow';
+};
+
+/**
+ * Function: isAddPointEvent
+ * 
+ * Returns true if the given event is a trigger to add a new point. This
+ * implementation returns true if shift is pressed.
+ */
+mxEdgeHandler.prototype.isAddPointEvent = function(evt)
+{
+	return mxEvent.isShiftDown(evt);
+};
+
+/**
+ * Function: isRemovePointEvent
+ * 
+ * Returns true if the given event is a trigger to remove a point. This
+ * implementation returns true if shift is pressed.
+ */
+mxEdgeHandler.prototype.isRemovePointEvent = function(evt)
+{
+	return mxEvent.isShiftDown(evt);
+};
+
+/**
+ * Function: getSelectionPoints
+ * 
+ * Returns the list of points that defines the selection stroke.
+ */
+mxEdgeHandler.prototype.getSelectionPoints = function(state)
+{
+	return state.absolutePoints;
+};
+
+/**
+ * Function: createSelectionShape
+ * 
+ * Creates the shape used to draw the selection border.
+ */
+mxEdgeHandler.prototype.createParentHighlightShape = function(bounds)
+{
+	var shape = new mxRectangleShape(bounds, null, this.getSelectionColor());
+	shape.strokewidth = this.getSelectionStrokeWidth();
+	shape.isDashed = this.isSelectionDashed();
+	
+	return shape;
+};
+
+/**
+ * Function: createSelectionShape
+ * 
+ * Creates the shape used to draw the selection border.
+ */
+mxEdgeHandler.prototype.createSelectionShape = function(points)
+{
+	var shape = new this.state.shape.constructor();
+	shape.outline = true;
+	shape.apply(this.state);
+	
+	shape.isDashed = this.isSelectionDashed();
+	shape.stroke = this.getSelectionColor();
+	shape.isShadow = false;
+	
+	return shape;
+};
+
+/**
+ * Function: getSelectionColor
+ * 
+ * Returns <mxConstants.EDGE_SELECTION_COLOR>.
+ */
+mxEdgeHandler.prototype.getSelectionColor = function()
+{
+	return mxConstants.EDGE_SELECTION_COLOR;
+};
+
+/**
+ * Function: getSelectionStrokeWidth
+ * 
+ * Returns <mxConstants.EDGE_SELECTION_STROKEWIDTH>.
+ */
+mxEdgeHandler.prototype.getSelectionStrokeWidth = function()
+{
+	return mxConstants.EDGE_SELECTION_STROKEWIDTH;
+};
+
+/**
+ * Function: isSelectionDashed
+ * 
+ * Returns <mxConstants.EDGE_SELECTION_DASHED>.
+ */
+mxEdgeHandler.prototype.isSelectionDashed = function()
+{
+	return mxConstants.EDGE_SELECTION_DASHED;
+};
+
+/**
+ * Function: isConnectableCell
+ * 
+ * Returns true if the given cell is connectable. This is a hook to
+ * disable floating connections. This implementation returns true.
+ */
+mxEdgeHandler.prototype.isConnectableCell = function(cell)
+{
+	return true;
+};
+
+/**
+ * Function: getCellAt
+ * 
+ * Creates and returns the <mxCellMarker> used in <marker>.
+ */
+mxEdgeHandler.prototype.getCellAt = function(x, y)
+{
+	return (!this.outlineConnect) ? this.graph.getCellAt(x, y) : null;
+};
+
+/**
+ * Function: createMarker
+ * 
+ * Creates and returns the <mxCellMarker> used in <marker>.
+ */
+mxEdgeHandler.prototype.createMarker = function()
+{
+	var marker = new mxCellMarker(this.graph);
+	var self = this; // closure
+
+	// Only returns edges if they are connectable and never returns
+	// the edge that is currently being modified
+	marker.getCell = function(me)
+	{
+		var cell = mxCellMarker.prototype.getCell.apply(this, arguments);
+
+		// Checks for cell at preview point (with grid)
+		if ((cell == self.state.cell || cell == null) && self.currentPoint != null)
+		{
+			cell = self.graph.getCellAt(self.currentPoint.x, self.currentPoint.y);
+		}
+		
+		// Uses connectable parent vertex if one exists
+		if (cell != null && !this.graph.isCellConnectable(cell))
+		{
+			var parent = this.graph.getModel().getParent(cell);
+			
+			if (this.graph.getModel().isVertex(parent) && this.graph.isCellConnectable(parent))
+			{
+				cell = parent;
+			}
+		}
+		
+		var model = self.graph.getModel();
+		
+		if ((this.graph.isSwimlane(cell) && self.currentPoint != null &&
+			this.graph.hitsSwimlaneContent(cell, self.currentPoint.x, self.currentPoint.y)) ||
+			(!self.isConnectableCell(cell)) || (cell == self.state.cell ||
+			(cell != null && !self.graph.connectableEdges && model.isEdge(cell))) ||
+			model.isAncestor(self.state.cell, cell))
+		{
+			cell = null;
+		}
+		
+		if (!this.graph.isCellConnectable(cell))
+		{
+			cell = null;
+		}
+		
+		return cell;
+	};
+
+	// Sets the highlight color according to validateConnection
+	marker.isValidState = function(state)
+	{
+		var model = self.graph.getModel();
+		var other = self.graph.view.getTerminalPort(state,
+			self.graph.view.getState(model.getTerminal(self.state.cell,
+			!self.isSource)), !self.isSource);
+		var otherCell = (other != null) ? other.cell : null;
+		var source = (self.isSource) ? state.cell : otherCell;
+		var target = (self.isSource) ? otherCell : state.cell;
+		
+		// Updates the error message of the handler
+		self.error = self.validateConnection(source, target);
+
+		return self.error == null;
+	};
+	
+	return marker;
+};
+
+/**
+ * Function: validateConnection
+ * 
+ * Returns the error message or an empty string if the connection for the
+ * given source, target pair is not valid. Otherwise it returns null. This
+ * implementation uses <mxGraph.getEdgeValidationError>.
+ * 
+ * Parameters:
+ * 
+ * source - <mxCell> that represents the source terminal.
+ * target - <mxCell> that represents the target terminal.
+ */
+mxEdgeHandler.prototype.validateConnection = function(source, target)
+{
+	return this.graph.getEdgeValidationError(this.state.cell, source, target);
+};
+
+/**
+ * Function: createBends
+ * 
+ * Creates and returns the bends used for modifying the edge. This is
+ * typically an array of <mxRectangleShapes>.
+ */
+ mxEdgeHandler.prototype.createBends = function()
+ {
+	var cell = this.state.cell;
+	var bends = [];
+
+	for (var i = 0; i < this.abspoints.length; i++)
+	{
+		if (this.isHandleVisible(i))
+		{
+			var source = i == 0;
+			var target = i == this.abspoints.length - 1;
+			var terminal = source || target;
+
+			if (terminal || this.graph.isCellBendable(cell))
+			{
+				(mxUtils.bind(this, function(index)
+				{
+					var bend = this.createHandleShape(index);
+					this.initBend(bend, mxUtils.bind(this, mxUtils.bind(this, function()
+					{
+						if (this.dblClickRemoveEnabled)
+						{
+							this.removePoint(this.state, index);
+						}
+					})));
+	
+					if (this.isHandleEnabled(i))
+					{
+						bend.setCursor((terminal) ? mxConstants.CURSOR_TERMINAL_HANDLE : mxConstants.CURSOR_BEND_HANDLE);
+					}
+					
+					bends.push(bend);
+				
+					if (!terminal)
+					{
+						this.points.push(new mxPoint(0,0));
+						bend.node.style.visibility = 'hidden';
+					}
+				}))(i);
+			}
+		}
+	}
+
+	return bends;
+};
+
+/**
+ * Function: createVirtualBends
+ * 
+ * Creates and returns the bends used for modifying the edge. This is
+ * typically an array of <mxRectangleShapes>.
+ */
+ mxEdgeHandler.prototype.createVirtualBends = function()
+ {
+	var cell = this.state.cell;
+	var last = this.abspoints[0];
+	var bends = [];
+
+	if (this.graph.isCellBendable(cell))
+	{
+		for (var i = 1; i < this.abspoints.length; i++)
+		{
+			(mxUtils.bind(this, function(bend)
+			{
+				this.initBend(bend);
+				bend.setCursor(mxConstants.CURSOR_VIRTUAL_BEND_HANDLE);
+				bends.push(bend);
+			}))(this.createHandleShape());
+		}
+	}
+
+	return bends;
+};
+
+/**
+ * Function: isHandleEnabled
+ * 
+ * Creates the shape used to display the given bend.
+ */
+mxEdgeHandler.prototype.isHandleEnabled = function(index)
+{
+	return true;
+};
+
+/**
+ * Function: isHandleVisible
+ * 
+ * Returns true if the handle at the given index is visible.
+ */
+mxEdgeHandler.prototype.isHandleVisible = function(index)
+{
+	var source = this.state.getVisibleTerminalState(true);
+	var target = this.state.getVisibleTerminalState(false);
+	var geo = this.graph.getCellGeometry(this.state.cell);
+	var edgeStyle = (geo != null) ? this.graph.view.getEdgeStyle(this.state, geo.points, source, target) : null;
+
+	return edgeStyle != mxEdgeStyle.EntityRelation || index == 0 || index == this.abspoints.length - 1;
+};
+
+/**
+ * Function: createHandleShape
+ * 
+ * Creates the shape used to display the given bend. Note that the index may be
+ * null for special cases, such as when called from
+ * <mxElbowEdgeHandler.createVirtualBend>. Only images and rectangles should be
+ * returned if support for HTML labels with not foreign objects is required.
+ * Index if null for virtual handles.
+ */
+mxEdgeHandler.prototype.createHandleShape = function(index)
+{
+	if (this.handleImage != null)
+	{
+		var shape = new mxImageShape(new mxRectangle(0, 0, this.handleImage.width, this.handleImage.height), this.handleImage.src);
+		
+		// Allows HTML rendering of the images
+		shape.preserveImageAspect = false;
+
+		return shape;
+	}
+	else
+	{
+		var s = mxConstants.HANDLE_SIZE;
+		
+		if (this.preferHtml)
+		{
+			s -= 1;
+		}
+		
+		return new mxRectangleShape(new mxRectangle(0, 0, s, s), mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
+	}
+};
+
+/**
+ * Function: createLabelHandleShape
+ * 
+ * Creates the shape used to display the the label handle.
+ */
+mxEdgeHandler.prototype.createLabelHandleShape = function()
+{
+	if (this.labelHandleImage != null)
+	{
+		var shape = new mxImageShape(new mxRectangle(0, 0, this.labelHandleImage.width, this.labelHandleImage.height), this.labelHandleImage.src);
+		
+		// Allows HTML rendering of the images
+		shape.preserveImageAspect = false;
+
+		return shape;
+	}
+	else
+	{
+		var s = mxConstants.LABEL_HANDLE_SIZE;
+		return new mxRectangleShape(new mxRectangle(0, 0, s, s), mxConstants.LABEL_HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
+	}
+};
+
+/**
+ * Function: initBend
+ * 
+ * Helper method to initialize the given bend.
+ * 
+ * Parameters:
+ * 
+ * bend - <mxShape> that represents the bend to be initialized.
+ */
+mxEdgeHandler.prototype.initBend = function(bend, dblClick)
+{
+	if (this.preferHtml)
+	{
+		bend.dialect = mxConstants.DIALECT_STRICTHTML;
+		bend.init(this.graph.container);
+	}
+	else
+	{
+		bend.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+			mxConstants.DIALECT_MIXEDHTML : mxConstants.DIALECT_SVG;
+		bend.init(this.graph.getView().getOverlayPane());
+	}
+
+	mxEvent.redirectMouseEvents(bend.node, this.graph, this.state,
+			null, null, null, dblClick);
+	
+	// Fixes lost event tracking for images in quirks / IE8 standards
+	if (mxClient.IS_QUIRKS || document.documentMode == 8)
+	{
+		mxEvent.addListener(bend.node, 'dragstart', function(evt)
+		{
+			mxEvent.consume(evt);
+			
+			return false;
+		});
+	}
+	
+	if (mxClient.IS_TOUCH)
+	{
+		bend.node.setAttribute('pointer-events', 'none');
+	}
+};
+
+/**
+ * Function: getHandleForEvent
+ * 
+ * Returns the index of the handle for the given event.
+ */
+mxEdgeHandler.prototype.getHandleForEvent = function(me)
+{
+	// Connection highlight may consume events before they reach sizer handle
+	var tol = (!mxEvent.isMouseEvent(me.getEvent())) ? this.tolerance : 1;
+	var hit = (this.allowHandleBoundsCheck && (mxClient.IS_IE || tol > 0)) ?
+		new mxRectangle(me.getGraphX() - tol, me.getGraphY() - tol, 2 * tol, 2 * tol) : null;
+	var minDistSq = null;
+	var result = null;
+
+	function checkShape(shape)
+	{
+		if (shape != null && shape.node.style.display != 'none' && shape.node.style.visibility != 'hidden' &&
+			(me.isSource(shape) || (hit != null && mxUtils.intersects(shape.bounds, hit))))
+		{
+			var dx = me.getGraphX() - shape.bounds.getCenterX();
+			var dy = me.getGraphY() - shape.bounds.getCenterY();
+			var tmp = dx * dx + dy * dy;
+			
+			if (minDistSq == null || tmp <= minDistSq)
+			{
+				minDistSq = tmp;
+			
+				return true;
+			}
+		}
+		
+		return false;
+	}
+	
+	if (this.customHandles != null && this.isCustomHandleEvent(me))
+	{
+		// Inverse loop order to match display order
+		for (var i = this.customHandles.length - 1; i >= 0; i--)
+		{
+			if (checkShape(this.customHandles[i].shape))
+			{
+				// LATER: Return reference to active shape
+				return mxEvent.CUSTOM_HANDLE - i;
+			}
+		}
+	}
+
+	if (me.isSource(this.state.text) || checkShape(this.labelShape))
+	{
+		result = mxEvent.LABEL_HANDLE;
+	}
+	
+	if (this.bends != null)
+	{
+		for (var i = 0; i < this.bends.length; i++)
+		{
+			if (checkShape(this.bends[i]))
+			{
+				result = i;
+			}
+		}
+	}
+	
+	if (this.virtualBends != null && this.isAddVirtualBendEvent(me))
+	{
+		for (var i = 0; i < this.virtualBends.length; i++)
+		{
+			if (checkShape(this.virtualBends[i]))
+			{
+				result = mxEvent.VIRTUAL_HANDLE - i;
+			}
+		}
+	}
+
+	return result;
+};
+
+/**
+ * Function: isAddVirtualBendEvent
+ * 
+ * Returns true if the given event allows virtual bends to be added. This
+ * implementation returns true.
+ */
+mxEdgeHandler.prototype.isAddVirtualBendEvent = function(me)
+{
+	return true;
+};
+
+/**
+ * Function: isCustomHandleEvent
+ * 
+ * Returns true if the given event allows custom handles to be changed. This
+ * implementation returns true.
+ */
+mxEdgeHandler.prototype.isCustomHandleEvent = function(me)
+{
+	return true;
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by checking if a special element of the handler
+ * was clicked, in which case the index parameter is non-null. The
+ * indices may be one of <LABEL_HANDLE> or the number of the respective
+ * control point. The source and target points are used for reconnecting
+ * the edge.
+ */
+mxEdgeHandler.prototype.mouseDown = function(sender, me)
+{
+	var handle = this.getHandleForEvent(me);
+	
+	if (this.bends != null && this.bends[handle] != null)
+	{
+		var b = this.bends[handle].bounds;
+		this.snapPoint = new mxPoint(b.getCenterX(), b.getCenterY());
+	}
+	
+	if (this.addEnabled && handle == null && this.isAddPointEvent(me.getEvent()))
+	{
+		this.addPoint(this.state, me.getEvent());
+		me.consume();
+	}
+	else if (handle != null && !me.isConsumed() && this.graph.isEnabled())
+	{
+		if (this.removeEnabled && this.isRemovePointEvent(me.getEvent()))
+		{
+			this.removePoint(this.state, handle);
+		}
+		else if (handle != mxEvent.LABEL_HANDLE || this.graph.isLabelMovable(me.getCell()))
+		{
+			if (handle <= mxEvent.VIRTUAL_HANDLE)
+			{
+				mxUtils.setOpacity(this.virtualBends[mxEvent.VIRTUAL_HANDLE - handle].node, 100);
+			}
+			
+			this.start(me.getX(), me.getY(), handle);
+		}
+		
+		me.consume();
+	}
+};
+
+/**
+ * Function: start
+ * 
+ * Starts the handling of the mouse gesture.
+ */
+mxEdgeHandler.prototype.start = function(x, y, index)
+{
+	this.startX = x;
+	this.startY = y;
+
+	this.isSource = (this.bends == null) ? false : index == 0;
+	this.isTarget = (this.bends == null) ? false : index == this.bends.length - 1;
+	this.isLabel = index == mxEvent.LABEL_HANDLE;
+
+	if (this.isSource || this.isTarget)
+	{
+		var cell = this.state.cell;
+		var terminal = this.graph.model.getTerminal(cell, this.isSource);
+
+		if ((terminal == null && this.graph.isTerminalPointMovable(cell, this.isSource)) ||
+			(terminal != null && this.graph.isCellDisconnectable(cell, terminal, this.isSource)))
+		{
+			this.index = index;
+		}
+	}
+	else
+	{
+		this.index = index;
+	}
+	
+	// Hides other custom handles
+	if (this.index <= mxEvent.CUSTOM_HANDLE && this.index > mxEvent.VIRTUAL_HANDLE)
+	{
+		if (this.customHandles != null)
+		{
+			for (var i = 0; i < this.customHandles.length; i++)
+			{
+				if (i != mxEvent.CUSTOM_HANDLE - this.index)
+				{
+					this.customHandles[i].setVisible(false);
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: clonePreviewState
+ * 
+ * Returns a clone of the current preview state for the given point and terminal.
+ */
+mxEdgeHandler.prototype.clonePreviewState = function(point, terminal)
+{
+	return this.state.clone();
+};
+
+/**
+ * Function: getSnapToTerminalTolerance
+ * 
+ * Returns the tolerance for the guides. Default value is
+ * gridSize * scale / 2.
+ */
+mxEdgeHandler.prototype.getSnapToTerminalTolerance = function()
+{
+	return this.graph.gridSize * this.graph.view.scale / 2;
+};
+
+/**
+ * Function: updateHint
+ * 
+ * Hook for subclassers do show details while the handler is active.
+ */
+mxEdgeHandler.prototype.updateHint = function(me, point) { };
+
+/**
+ * Function: removeHint
+ * 
+ * Hooks for subclassers to hide details when the handler gets inactive.
+ */
+mxEdgeHandler.prototype.removeHint = function() { };
+
+/**
+ * Function: roundLength
+ * 
+ * Hook for rounding the unscaled width or height. This uses Math.round.
+ */
+mxEdgeHandler.prototype.roundLength = function(length)
+{
+	return Math.round(length);
+};
+
+/**
+ * Function: isSnapToTerminalsEvent
+ * 
+ * Returns true if <snapToTerminals> is true and if alt is not pressed.
+ */
+mxEdgeHandler.prototype.isSnapToTerminalsEvent = function(me)
+{
+	return this.snapToTerminals && !mxEvent.isAltDown(me.getEvent());
+};
+
+/**
+ * Function: getPointForEvent
+ * 
+ * Returns the point for the given event.
+ */
+mxEdgeHandler.prototype.getPointForEvent = function(me)
+{
+	var view = this.graph.getView();
+	var scale = view.scale;
+	var point = new mxPoint(this.roundLength(me.getGraphX() / scale) * scale,
+		this.roundLength(me.getGraphY() / scale) * scale);
+	
+	var tt = this.getSnapToTerminalTolerance();
+	var overrideX = false;
+	var overrideY = false;		
+	
+	if (tt > 0 && this.isSnapToTerminalsEvent(me))
+	{
+		function snapToPoint(pt)
+		{
+			if (pt != null)
+			{
+				var x = pt.x;
+
+				if (Math.abs(point.x - x) < tt)
+				{
+					point.x = x;
+					overrideX = true;
+				}
+				
+				var y = pt.y;
+
+				if (Math.abs(point.y - y) < tt)
+				{
+					point.y = y;
+					overrideY = true;
+				}
+			}
+		}
+		
+		// Temporary function
+		function snapToTerminal(terminal)
+		{
+			if (terminal != null)
+			{
+				snapToPoint.call(this, new mxPoint(view.getRoutingCenterX(terminal),
+						view.getRoutingCenterY(terminal)));
+			}
+		};
+
+		snapToTerminal.call(this, this.state.getVisibleTerminalState(true));
+		snapToTerminal.call(this, this.state.getVisibleTerminalState(false));
+
+		if (this.state.absolutePoints != null)
+		{
+			for (var i = 0; i < this.state.absolutePoints.length; i++)
+			{
+				snapToPoint.call(this, this.state.absolutePoints[i]);
+			}
+		}
+	}
+
+	if (this.graph.isGridEnabledEvent(me.getEvent()))
+	{
+		var tr = view.translate;
+		
+		if (!overrideX)
+		{
+			point.x = (this.graph.snap(point.x / scale - tr.x) + tr.x) * scale;
+		}
+		
+		if (!overrideY)
+		{
+			point.y = (this.graph.snap(point.y / scale - tr.y) + tr.y) * scale;
+		}
+	}
+	
+	return point;
+};
+
+/**
+ * Function: getPreviewTerminalState
+ * 
+ * Updates the given preview state taking into account the state of the constraint handler.
+ */
+mxEdgeHandler.prototype.getPreviewTerminalState = function(me)
+{
+	this.constraintHandler.update(me, this.isSource, true, me.isSource(this.marker.highlight.shape) ? null : this.currentPoint);
+	
+	if (this.constraintHandler.currentFocus != null && this.constraintHandler.currentConstraint != null)
+	{
+		// Handles special case where grid is large and connection point is at actual point in which
+		// case the outline is not followed as long as we're < gridSize / 2 away from that point
+		if (this.marker.highlight != null && this.marker.highlight.state != null &&
+			this.marker.highlight.state.cell == this.constraintHandler.currentFocus.cell)
+		{
+			// Direct repaint needed if cell already highlighted
+			if (this.marker.highlight.shape.stroke != 'transparent')
+			{
+				this.marker.highlight.shape.stroke = 'transparent';
+				this.marker.highlight.repaint();
+			}
+		}
+		else
+		{
+			this.marker.markCell(this.constraintHandler.currentFocus.cell, 'transparent');
+		}
+		
+		var model = this.graph.getModel();
+		var other = this.graph.view.getTerminalPort(this.state,
+				this.graph.view.getState(model.getTerminal(this.state.cell,
+			!this.isSource)), !this.isSource);
+		var otherCell = (other != null) ? other.cell : null;
+		var source = (this.isSource) ? this.constraintHandler.currentFocus.cell : otherCell;
+		var target = (this.isSource) ? otherCell : this.constraintHandler.currentFocus.cell;
+		
+		// Updates the error message of the handler
+		this.error = this.validateConnection(source, target);
+		var result = null;
+		
+		if (this.error == null)
+		{
+			result = this.constraintHandler.currentFocus;
+		}
+		else
+		{
+			this.constraintHandler.reset();
+		}
+		
+		return result;
+	}
+	else if (!this.graph.isIgnoreTerminalEvent(me.getEvent()))
+	{
+		this.marker.process(me);
+
+		return this.marker.getValidState();
+	}
+	else
+	{
+		this.marker.reset();
+		
+		return null;
+	}
+};
+
+/**
+ * Function: getPreviewPoints
+ * 
+ * Updates the given preview state taking into account the state of the constraint handler.
+ * 
+ * Parameters:
+ * 
+ * pt - <mxPoint> that contains the current pointer position.
+ * me - Optional <mxMouseEvent> that contains the current event.
+ */
+mxEdgeHandler.prototype.getPreviewPoints = function(pt, me)
+{
+	var geometry = this.graph.getCellGeometry(this.state.cell);
+	var points = (geometry.points != null) ? geometry.points.slice() : null;
+	var point = new mxPoint(pt.x, pt.y);
+	var result = null;
+	
+	if (!this.isSource && !this.isTarget)
+	{
+		this.convertPoint(point, false);
+		
+		if (points == null)
+		{
+			points = [point];
+		}
+		else
+		{
+			// Adds point from virtual bend
+			if (this.index <= mxEvent.VIRTUAL_HANDLE)
+			{
+				points.splice(mxEvent.VIRTUAL_HANDLE - this.index, 0, point);
+			}
+
+			// Removes point if dragged on terminal point
+			if (!this.isSource && !this.isTarget)
+			{
+				for (var i = 0; i < this.bends.length; i++)
+				{
+					if (i != this.index)
+					{
+						var bend = this.bends[i];
+						
+						if (bend != null && mxUtils.contains(bend.bounds, pt.x, pt.y))
+						{
+							if (this.index <= mxEvent.VIRTUAL_HANDLE)
+							{
+								points.splice(mxEvent.VIRTUAL_HANDLE - this.index, 1);
+							}
+							else
+							{
+								points.splice(this.index - 1, 1);
+							}
+							
+							result = points;
+						}
+					}
+				}
+				
+				// Removes point if user tries to straighten a segment
+				if (result == null && this.straightRemoveEnabled && (me == null || !mxEvent.isAltDown(me.getEvent())))
+				{
+					var tol = this.graph.tolerance * this.graph.tolerance;
+					var abs = this.state.absolutePoints.slice();
+					abs[this.index] = pt;
+					
+					// Handes special case where removing waypoint affects tolerance (flickering)
+					var src = this.state.getVisibleTerminalState(true);
+					
+					if (src != null)
+					{
+						var c = this.graph.getConnectionConstraint(this.state, src, true);
+						
+						// Checks if point is not fixed
+						if (c == null || this.graph.getConnectionPoint(src, c) == null)
+						{
+							abs[0] = new mxPoint(src.view.getRoutingCenterX(src), src.view.getRoutingCenterY(src));
+						}
+					}
+					
+					var trg = this.state.getVisibleTerminalState(false);
+					
+					if (trg != null)
+					{
+						var c = this.graph.getConnectionConstraint(this.state, trg, false);
+						
+						// Checks if point is not fixed
+						if (c == null || this.graph.getConnectionPoint(trg, c) == null)
+						{
+							abs[abs.length - 1] = new mxPoint(trg.view.getRoutingCenterX(trg), trg.view.getRoutingCenterY(trg));
+						}
+					}
+
+					function checkRemove(idx, tmp)
+					{
+						if (idx > 0 && idx < abs.length - 1 &&
+							mxUtils.ptSegDistSq(abs[idx - 1].x, abs[idx - 1].y,
+								abs[idx + 1].x, abs[idx + 1].y, tmp.x, tmp.y) < tol)
+						{
+							points.splice(idx - 1, 1);
+							result = points;
+						}
+					};
+					
+					// LATER: Check if other points can be removed if a segment is made straight
+					checkRemove(this.index, pt);
+				}
+			}
+			
+			// Updates existing point
+			if (result == null && this.index > mxEvent.VIRTUAL_HANDLE)
+			{
+				points[this.index - 1] = point;
+			}
+		}
+	}
+	else if (this.graph.resetEdgesOnConnect)
+	{
+		points = null;
+	}
+	
+	return (result != null) ? result : points;
+};
+
+/**
+ * Function: isOutlineConnectEvent
+ * 
+ * Returns true if <outlineConnect> is true and the source of the event is the outline shape
+ * or shift is pressed.
+ */
+mxEdgeHandler.prototype.isOutlineConnectEvent = function(me)
+{
+	var offset = mxUtils.getOffset(this.graph.container);
+	var evt = me.getEvent();
+	
+	var clientX = mxEvent.getClientX(evt);
+	var clientY = mxEvent.getClientY(evt);
+	
+	var doc = document.documentElement;
+	var left = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
+	var top = (window.pageYOffset || doc.scrollTop)  - (doc.clientTop || 0);
+	
+	var gridX = this.currentPoint.x - this.graph.container.scrollLeft + offset.x - left;
+	var gridY = this.currentPoint.y - this.graph.container.scrollTop + offset.y - top;
+
+	return this.outlineConnect && !mxEvent.isShiftDown(me.getEvent()) &&
+		(me.isSource(this.marker.highlight.shape) ||
+		(mxEvent.isAltDown(me.getEvent()) && me.getState() != null) ||
+		this.marker.highlight.isHighlightAt(clientX, clientY) ||
+		((gridX != clientX || gridY != clientY) && me.getState() == null &&
+		this.marker.highlight.isHighlightAt(gridX, gridY)));
+};
+
+/**
+ * Function: updatePreviewState
+ * 
+ * Updates the given preview state taking into account the state of the constraint handler.
+ */
+mxEdgeHandler.prototype.updatePreviewState = function(edge, point, terminalState, me, outline)
+{
+	// Computes the points for the edge style and terminals
+	var sourceState = (this.isSource) ? terminalState : this.state.getVisibleTerminalState(true);
+	var targetState = (this.isTarget) ? terminalState : this.state.getVisibleTerminalState(false);
+	
+	var sourceConstraint = this.graph.getConnectionConstraint(edge, sourceState, true);
+	var targetConstraint = this.graph.getConnectionConstraint(edge, targetState, false);
+
+	var constraint = this.constraintHandler.currentConstraint;
+
+	if (constraint == null && outline)
+	{
+		if (terminalState != null)
+		{
+			// Handles special case where mouse is on outline away from actual end point
+			// in which case the grid is ignored and mouse point is used instead
+			if (me.isSource(this.marker.highlight.shape))
+			{
+				point = new mxPoint(me.getGraphX(), me.getGraphY());
+			}
+			
+			constraint = this.graph.getOutlineConstraint(point, terminalState, me);
+			this.constraintHandler.setFocus(me, terminalState, this.isSource);
+			this.constraintHandler.currentConstraint = constraint;
+			this.constraintHandler.currentPoint = point;
+		}
+		else
+		{
+			constraint = new mxConnectionConstraint();
+		}
+	}
+	
+	if (this.outlineConnect && this.marker.highlight != null && this.marker.highlight.shape != null)
+	{
+		var s = this.graph.view.scale;
+		
+		if (this.constraintHandler.currentConstraint != null &&
+			this.constraintHandler.currentFocus != null)
+		{
+			this.marker.highlight.shape.stroke = (outline) ? mxConstants.OUTLINE_HIGHLIGHT_COLOR : 'transparent';
+			this.marker.highlight.shape.strokewidth = mxConstants.OUTLINE_HIGHLIGHT_STROKEWIDTH / s / s;
+			this.marker.highlight.repaint();
+		}
+		else if (this.marker.hasValidState())
+		{
+			this.marker.highlight.shape.stroke = (this.marker.getValidState() == me.getState()) ?
+				mxConstants.DEFAULT_VALID_COLOR : 'transparent';
+			this.marker.highlight.shape.strokewidth = mxConstants.HIGHLIGHT_STROKEWIDTH / s / s;
+			this.marker.highlight.repaint();
+		}
+	}
+	
+	if (this.isSource)
+	{
+		sourceConstraint = constraint;
+	}
+	else if (this.isTarget)
+	{
+		targetConstraint = constraint;
+	}
+	
+	if (this.isSource || this.isTarget)
+	{
+		if (constraint != null && constraint.point != null)
+		{
+			edge.style[(this.isSource) ? mxConstants.STYLE_EXIT_X : mxConstants.STYLE_ENTRY_X] = constraint.point.x;
+			edge.style[(this.isSource) ? mxConstants.STYLE_EXIT_Y : mxConstants.STYLE_ENTRY_Y] = constraint.point.y;
+		}
+		else
+		{
+			delete edge.style[(this.isSource) ? mxConstants.STYLE_EXIT_X : mxConstants.STYLE_ENTRY_X];
+			delete edge.style[(this.isSource) ? mxConstants.STYLE_EXIT_Y : mxConstants.STYLE_ENTRY_Y];
+		}
+	}
+	
+	edge.setVisibleTerminalState(sourceState, true);
+	edge.setVisibleTerminalState(targetState, false);
+	
+	if (!this.isSource || sourceState != null)
+	{
+		edge.view.updateFixedTerminalPoint(edge, sourceState, true, sourceConstraint);
+	}
+	
+	if (!this.isTarget || targetState != null)
+	{
+		edge.view.updateFixedTerminalPoint(edge, targetState, false, targetConstraint);
+	}
+	
+	if ((this.isSource || this.isTarget) && terminalState == null)
+	{
+		edge.setAbsoluteTerminalPoint(point, this.isSource);
+
+		if (this.marker.getMarkedState() == null)
+		{
+			this.error = (this.graph.allowDanglingEdges) ? null : '';
+		}
+	}
+	
+	edge.view.updatePoints(edge, this.points, sourceState, targetState);
+	edge.view.updateFloatingTerminalPoints(edge, sourceState, targetState);
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating the preview.
+ */
+mxEdgeHandler.prototype.mouseMove = function(sender, me)
+{
+	if (this.index != null && this.marker != null)
+	{
+		this.currentPoint = this.getPointForEvent(me);
+		this.error = null;
+		
+		// Uses the current point from the constraint handler if available
+		if (!this.graph.isIgnoreTerminalEvent(me.getEvent()) && mxEvent.isShiftDown(me.getEvent()) && this.snapPoint != null)
+		{
+			if (Math.abs(this.snapPoint.x - this.currentPoint.x) < Math.abs(this.snapPoint.y - this.currentPoint.y))
+			{
+				this.currentPoint.x = this.snapPoint.x;
+			}
+			else
+			{
+				this.currentPoint.y = this.snapPoint.y;
+			}
+		}
+		
+		if (this.index <= mxEvent.CUSTOM_HANDLE && this.index > mxEvent.VIRTUAL_HANDLE)
+		{
+			if (this.customHandles != null)
+			{
+				this.customHandles[mxEvent.CUSTOM_HANDLE - this.index].processEvent(me);
+			}
+		}
+		else if (this.isLabel)
+		{
+			this.label.x = this.currentPoint.x;
+			this.label.y = this.currentPoint.y;
+		}
+		else
+		{
+			this.points = this.getPreviewPoints(this.currentPoint, me);
+			var terminalState = (this.isSource || this.isTarget) ? this.getPreviewTerminalState(me) : null;
+
+			if (this.constraintHandler.currentConstraint != null &&
+				this.constraintHandler.currentFocus != null &&
+				this.constraintHandler.currentPoint != null)
+			{
+				this.currentPoint = this.constraintHandler.currentPoint.clone();
+			}
+			else if (this.outlineConnect)
+			{
+				// Need to check outline before cloning terminal state
+				var outline = (this.isSource || this.isTarget) ? this.isOutlineConnectEvent(me) : false
+						
+				if (outline)
+				{
+					terminalState = this.marker.highlight.state;
+				}
+				else if (terminalState != null && terminalState != me.getState() && this.marker.highlight.shape != null)
+				{
+					this.marker.highlight.shape.stroke = 'transparent';
+					this.marker.highlight.repaint();
+					terminalState = null;
+				}
+			}
+			
+			var clone = this.clonePreviewState(this.currentPoint, (terminalState != null) ? terminalState.cell : null);
+			this.updatePreviewState(clone, this.currentPoint, terminalState, me, outline);
+
+			// Sets the color of the preview to valid or invalid, updates the
+			// points of the preview and redraws
+			var color = (this.error == null) ? this.marker.validColor : this.marker.invalidColor;
+			this.setPreviewColor(color);
+			this.abspoints = clone.absolutePoints;
+			this.active = true;
+		}
+
+		// This should go before calling isOutlineConnectEvent above. As a workaround
+		// we add an offset of gridSize to the hint to avoid problem with hit detection
+		// in highlight.isHighlightAt (which uses comonentFromPoint)
+		this.updateHint(me, this.currentPoint);
+		this.drawPreview();
+		mxEvent.consume(me.getEvent());
+		me.consume();
+	}
+	// Workaround for disabling the connect highlight when over handle
+	else if (mxClient.IS_IE && this.getHandleForEvent(me) != null)
+	{
+		me.consume(false);
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event to applying the previewed changes on the edge by
+ * using <moveLabel>, <connect> or <changePoints>.
+ */
+mxEdgeHandler.prototype.mouseUp = function(sender, me)
+{
+	// Workaround for wrong event source in Webkit
+	if (this.index != null && this.marker != null)
+	{
+		var edge = this.state.cell;
+		
+		// Ignores event if mouse has not been moved
+		if (me.getX() != this.startX || me.getY() != this.startY)
+		{
+			var clone = !this.graph.isIgnoreTerminalEvent(me.getEvent()) && this.graph.isCloneEvent(me.getEvent()) &&
+				this.cloneEnabled && this.graph.isCellsCloneable();
+			
+			// Displays the reason for not carriying out the change
+			// if there is an error message with non-zero length
+			if (this.error != null)
+			{
+				if (this.error.length > 0)
+				{
+					this.graph.validationAlert(this.error);
+				}
+			}
+			else if (this.index <= mxEvent.CUSTOM_HANDLE && this.index > mxEvent.VIRTUAL_HANDLE)
+			{
+				if (this.customHandles != null)
+				{
+					var model = this.graph.getModel();
+					
+					model.beginUpdate();
+					try
+					{
+						this.customHandles[mxEvent.CUSTOM_HANDLE - this.index].execute();
+					}
+					finally
+					{
+						model.endUpdate();
+					}
+				}
+			}
+			else if (this.isLabel)
+			{
+				this.moveLabel(this.state, this.label.x, this.label.y);
+			}
+			else if (this.isSource || this.isTarget)
+			{
+				var terminal = null;
+				
+				if (this.constraintHandler.currentConstraint != null &&
+					this.constraintHandler.currentFocus != null)
+				{
+					terminal = this.constraintHandler.currentFocus.cell;
+				}
+				
+				if (terminal == null && this.marker.hasValidState() && this.marker.highlight != null &&
+					this.marker.highlight.shape != null &&
+					this.marker.highlight.shape.stroke != 'transparent' &&
+					this.marker.highlight.shape.stroke != 'white')
+				{
+					terminal = this.marker.validState.cell;
+				}
+				
+				if (terminal != null)
+				{
+					edge = this.connect(edge, terminal, this.isSource, clone, me);
+				}
+				else if (this.graph.isAllowDanglingEdges())
+				{
+					var pt = this.abspoints[(this.isSource) ? 0 : this.abspoints.length - 1];
+					pt.x = this.roundLength(pt.x / this.graph.view.scale - this.graph.view.translate.x);
+					pt.y = this.roundLength(pt.y / this.graph.view.scale - this.graph.view.translate.y);
+
+					var pstate = this.graph.getView().getState(
+							this.graph.getModel().getParent(edge));
+							
+					if (pstate != null)
+					{
+						pt.x -= pstate.origin.x;
+						pt.y -= pstate.origin.y;
+					}
+					
+					pt.x -= this.graph.panDx / this.graph.view.scale;
+					pt.y -= this.graph.panDy / this.graph.view.scale;
+										
+					// Destroys and recreates this handler
+					edge = this.changeTerminalPoint(edge, pt, this.isSource, clone);
+				}
+			}
+			else if (this.active)
+			{
+				edge = this.changePoints(edge, this.points, clone);
+			}
+			else
+			{
+				this.graph.getView().invalidate(this.state.cell);
+				this.graph.getView().validate(this.state.cell);						
+			}
+		}
+		
+		// Resets the preview color the state of the handler if this
+		// handler has not been recreated
+		if (this.marker != null)
+		{
+			this.reset();
+
+			// Updates the selection if the edge has been cloned
+			if (edge != this.state.cell)
+			{
+				this.graph.setSelectionCell(edge);
+			}
+		}
+
+		me.consume();
+	}
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of this handler.
+ */
+mxEdgeHandler.prototype.reset = function()
+{
+	this.error = null;
+	this.index = null;
+	this.label = null;
+	this.points = null;
+	this.snapPoint = null;
+	this.active = false;
+	this.isLabel = false;
+	this.isSource = false;
+	this.isTarget = false;
+	
+	if (this.livePreview && this.sizers != null)
+	{
+		for (var i = 0; i < this.sizers.length; i++)
+		{
+			if (this.sizers[i] != null)
+			{
+				this.sizers[i].node.style.display = '';
+			}
+		}
+	}
+
+	if (this.marker != null)
+	{
+		this.marker.reset();
+	}
+	
+	if (this.constraintHandler != null)
+	{
+		this.constraintHandler.reset();
+	}
+	
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			this.customHandles[i].reset();
+		}
+	}
+
+	this.setPreviewColor(mxConstants.EDGE_SELECTION_COLOR);
+	this.removeHint();
+	this.redraw();
+};
+
+/**
+ * Function: setPreviewColor
+ * 
+ * Sets the color of the preview to the given value.
+ */
+mxEdgeHandler.prototype.setPreviewColor = function(color)
+{
+	if (this.shape != null)
+	{
+		this.shape.stroke = color;
+	}
+};
+
+
+/**
+ * Function: convertPoint
+ * 
+ * Converts the given point in-place from screen to unscaled, untranslated
+ * graph coordinates and applies the grid. Returns the given, modified
+ * point instance.
+ * 
+ * Parameters:
+ * 
+ * point - <mxPoint> to be converted.
+ * gridEnabled - Boolean that specifies if the grid should be applied.
+ */
+mxEdgeHandler.prototype.convertPoint = function(point, gridEnabled)
+{
+	var scale = this.graph.getView().getScale();
+	var tr = this.graph.getView().getTranslate();
+		
+	if (gridEnabled)
+	{
+		point.x = this.graph.snap(point.x);
+		point.y = this.graph.snap(point.y);
+	}
+	
+	point.x = Math.round(point.x / scale - tr.x);
+	point.y = Math.round(point.y / scale - tr.y);
+
+	var pstate = this.graph.getView().getState(
+		this.graph.getModel().getParent(this.state.cell));
+
+	if (pstate != null)
+	{
+		point.x -= pstate.origin.x;
+		point.y -= pstate.origin.y;
+	}
+
+	return point;
+};
+
+/**
+ * Function: moveLabel
+ * 
+ * Changes the coordinates for the label of the given edge.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> that represents the edge.
+ * x - Integer that specifies the x-coordinate of the new location.
+ * y - Integer that specifies the y-coordinate of the new location.
+ */
+mxEdgeHandler.prototype.moveLabel = function(edgeState, x, y)
+{
+	var model = this.graph.getModel();
+	var geometry = model.getGeometry(edgeState.cell);
+	
+	if (geometry != null)
+	{
+		var scale = this.graph.getView().scale;
+		geometry = geometry.clone();
+		
+		if (geometry.relative)
+		{
+			// Resets the relative location stored inside the geometry
+			var pt = this.graph.getView().getRelativePoint(edgeState, x, y);
+			geometry.x = Math.round(pt.x * 10000) / 10000;
+			geometry.y = Math.round(pt.y);
+			
+			// Resets the offset inside the geometry to find the offset
+			// from the resulting point
+			geometry.offset = new mxPoint(0, 0);
+			var pt = this.graph.view.getPoint(edgeState, geometry);
+			geometry.offset = new mxPoint(Math.round((x - pt.x) / scale), Math.round((y - pt.y) / scale));
+		}
+		else
+		{
+			var points = edgeState.absolutePoints;
+			var p0 = points[0];
+			var pe = points[points.length - 1];
+			
+			if (p0 != null && pe != null)
+			{
+				var cx = p0.x + (pe.x - p0.x) / 2;
+				var cy = p0.y + (pe.y - p0.y) / 2;
+				
+				geometry.offset = new mxPoint(Math.round((x - cx) / scale), Math.round((y - cy) / scale));
+				geometry.x = 0;
+				geometry.y = 0;
+			}
+		}
+
+		model.setGeometry(edgeState.cell, geometry);
+	}
+};
+
+/**
+ * Function: connect
+ * 
+ * Changes the terminal or terminal point of the given edge in the graph
+ * model.
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> that represents the edge to be reconnected.
+ * terminal - <mxCell> that represents the new terminal.
+ * isSource - Boolean indicating if the new terminal is the source or
+ * target terminal.
+ * isClone - Boolean indicating if the new connection should be a clone of
+ * the old edge.
+ * me - <mxMouseEvent> that contains the mouse up event.
+ */
+mxEdgeHandler.prototype.connect = function(edge, terminal, isSource, isClone, me)
+{
+	var model = this.graph.getModel();
+	var parent = model.getParent(edge);
+	
+	model.beginUpdate();
+	try
+	{
+		// Clones and adds the cell
+		if (isClone)
+		{
+			var clone = this.graph.cloneCells([edge])[0];
+			model.add(parent, clone, model.getChildCount(parent));
+			
+			var other = model.getTerminal(edge, !isSource);
+			this.graph.connectCell(clone, other, !isSource);
+			
+			edge = clone;
+		}
+
+		var constraint = this.constraintHandler.currentConstraint;
+		
+		if (constraint == null)
+		{
+			constraint = new mxConnectionConstraint();
+		}
+
+		this.graph.connectCell(edge, terminal, isSource, constraint);
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+	
+	return edge;
+};
+
+/**
+ * Function: changeTerminalPoint
+ * 
+ * Changes the terminal point of the given edge.
+ */
+mxEdgeHandler.prototype.changeTerminalPoint = function(edge, point, isSource, clone)
+{
+	var model = this.graph.getModel();
+
+	model.beginUpdate();
+	try
+	{
+		if (clone)
+		{
+			var parent = model.getParent(edge);
+			var terminal = model.getTerminal(edge, !isSource);
+			edge = this.graph.cloneCells([edge])[0];
+			model.add(parent, edge, model.getChildCount(parent));
+			model.setTerminal(edge, terminal, !isSource);
+		}
+
+		var geo = model.getGeometry(edge);
+		
+		if (geo != null)
+		{
+			geo = geo.clone();
+			geo.setTerminalPoint(point, isSource);
+			model.setGeometry(edge, geo);
+			this.graph.connectCell(edge, null, isSource, new mxConnectionConstraint());
+		}
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+	
+	return edge;
+};
+
+/**
+ * Function: changePoints
+ * 
+ * Changes the control points of the given edge in the graph model.
+ */
+mxEdgeHandler.prototype.changePoints = function(edge, points, clone)
+{
+	var model = this.graph.getModel();
+	model.beginUpdate();
+	try
+	{
+		if (clone)
+		{
+			var parent = model.getParent(edge);
+			var source = model.getTerminal(edge, true);
+			var target = model.getTerminal(edge, false);
+			edge = this.graph.cloneCells([edge])[0];
+			model.add(parent, edge, model.getChildCount(parent));
+			model.setTerminal(edge, source, true);
+			model.setTerminal(edge, target, false);
+		}
+		
+		var geo = model.getGeometry(edge);
+		
+		if (geo != null)
+		{
+			geo = geo.clone();
+			geo.points = points;
+			
+			model.setGeometry(edge, geo);
+		}
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+	
+	return edge;
+};
+
+/**
+ * Function: addPoint
+ * 
+ * Adds a control point for the given state and event.
+ */
+mxEdgeHandler.prototype.addPoint = function(state, evt)
+{
+	var pt = mxUtils.convertPoint(this.graph.container, mxEvent.getClientX(evt),
+			mxEvent.getClientY(evt));
+	var gridEnabled = this.graph.isGridEnabledEvent(evt);
+	this.convertPoint(pt, gridEnabled);
+	this.addPointAt(state, pt.x, pt.y);
+	mxEvent.consume(evt);
+};
+
+/**
+ * Function: addPointAt
+ * 
+ * Adds a control point at the given point.
+ */
+mxEdgeHandler.prototype.addPointAt = function(state, x, y)
+{
+	var geo = this.graph.getCellGeometry(state.cell);
+	var pt = new mxPoint(x, y);
+	
+	if (geo != null)
+	{
+		geo = geo.clone();
+		var t = this.graph.view.translate;
+		var s = this.graph.view.scale;
+		var offset = new mxPoint(t.x * s, t.y * s);
+		
+		var parent = this.graph.model.getParent(this.state.cell);
+		
+		if (this.graph.model.isVertex(parent))
+		{
+			var pState = this.graph.view.getState(parent);
+			offset = new mxPoint(pState.x, pState.y);
+		}
+		
+		var index = mxUtils.findNearestSegment(state, pt.x * s + offset.x, pt.y * s + offset.y);
+
+		if (geo.points == null)
+		{
+			geo.points = [pt];
+		}
+		else
+		{
+			geo.points.splice(index, 0, pt);
+		}
+		
+		this.graph.getModel().setGeometry(state.cell, geo);
+		this.refresh();	
+		this.redraw();
+	}
+};
+
+/**
+ * Function: removePoint
+ * 
+ * Removes the control point at the given index from the given state.
+ */
+mxEdgeHandler.prototype.removePoint = function(state, index)
+{
+	if (index > 0 && index < this.abspoints.length - 1)
+	{
+		var geo = this.graph.getCellGeometry(this.state.cell);
+		
+		if (geo != null && geo.points != null)
+		{
+			geo = geo.clone();
+			geo.points.splice(index - 1, 1);
+			this.graph.getModel().setGeometry(state.cell, geo);
+			this.refresh();
+			this.redraw();
+		}
+	}
+};
+
+/**
+ * Function: getHandleFillColor
+ * 
+ * Returns the fillcolor for the handle at the given index.
+ */
+mxEdgeHandler.prototype.getHandleFillColor = function(index)
+{
+	var isSource = index == 0;
+	var cell = this.state.cell;
+	var terminal = this.graph.getModel().getTerminal(cell, isSource);
+	var color = mxConstants.HANDLE_FILLCOLOR;
+	
+	if ((terminal != null && !this.graph.isCellDisconnectable(cell, terminal, isSource)) ||
+		(terminal == null && !this.graph.isTerminalPointMovable(cell, isSource)))
+	{
+		color = mxConstants.LOCKED_HANDLE_FILLCOLOR;
+	}
+	else if (terminal != null && this.graph.isCellDisconnectable(cell, terminal, isSource))
+	{
+		color = mxConstants.CONNECT_HANDLE_FILLCOLOR;
+	}
+	
+	return color;
+};
+
+/**
+ * Function: redraw
+ * 
+ * Redraws the preview, and the bends- and label control points.
+ */
+mxEdgeHandler.prototype.redraw = function()
+{
+	this.abspoints = this.state.absolutePoints.slice();
+	this.redrawHandles();
+	
+	var g = this.graph.getModel().getGeometry(this.state.cell);
+	var pts = g.points;
+
+	if (this.bends != null && this.bends.length > 0)
+	{
+		if (pts != null)
+		{
+			if (this.points == null)
+			{
+				this.points = [];
+			}
+			
+			for (var i = 1; i < this.bends.length - 1; i++)
+			{
+				if (this.bends[i] != null && this.abspoints[i] != null)
+				{
+					this.points[i - 1] = pts[i - 1];
+				}
+			}
+		}
+	}
+
+	this.drawPreview();
+};
+
+/**
+ * Function: redrawHandles
+ * 
+ * Redraws the handles.
+ */
+mxEdgeHandler.prototype.redrawHandles = function()
+{
+	var cell = this.state.cell;
+
+	// Updates the handle for the label position
+	var b = this.labelShape.bounds;
+	this.label = new mxPoint(this.state.absoluteOffset.x, this.state.absoluteOffset.y);
+	this.labelShape.bounds = new mxRectangle(Math.round(this.label.x - b.width / 2),
+		Math.round(this.label.y - b.height / 2), b.width, b.height);
+
+	// Shows or hides the label handle depending on the label
+	var lab = this.graph.getLabel(cell);
+	this.labelShape.visible = (lab != null && lab.length > 0 && this.graph.isLabelMovable(cell));
+	
+	if (this.bends != null && this.bends.length > 0)
+	{
+		var n = this.abspoints.length - 1;
+		
+		var p0 = this.abspoints[0];
+		var x0 = p0.x;
+		var y0 = p0.y;
+		
+		b = this.bends[0].bounds;
+		this.bends[0].bounds = new mxRectangle(Math.floor(x0 - b.width / 2),
+				Math.floor(y0 - b.height / 2), b.width, b.height);
+		this.bends[0].fill = this.getHandleFillColor(0);
+		this.bends[0].redraw();
+		
+		if (this.manageLabelHandle)
+		{
+			this.checkLabelHandle(this.bends[0].bounds);
+		}
+				
+		var pe = this.abspoints[n];
+		var xn = pe.x;
+		var yn = pe.y;
+		
+		var bn = this.bends.length - 1;
+		b = this.bends[bn].bounds;
+		this.bends[bn].bounds = new mxRectangle(Math.floor(xn - b.width / 2),
+				Math.floor(yn - b.height / 2), b.width, b.height);
+		this.bends[bn].fill = this.getHandleFillColor(bn);
+		this.bends[bn].redraw();
+				
+		if (this.manageLabelHandle)
+		{
+			this.checkLabelHandle(this.bends[bn].bounds);
+		}
+		
+		this.redrawInnerBends(p0, pe);
+	}
+
+	if (this.abspoints != null && this.virtualBends != null && this.virtualBends.length > 0)
+	{
+		var last = this.abspoints[0];
+		
+		for (var i = 0; i < this.virtualBends.length; i++)
+		{
+			if (this.virtualBends[i] != null && this.abspoints[i + 1] != null)
+			{
+				var pt = this.abspoints[i + 1];
+				var b = this.virtualBends[i];
+				var x = last.x + (pt.x - last.x) / 2;
+				var y = last.y + (pt.y - last.y) / 2;
+				b.bounds = new mxRectangle(Math.floor(x - b.bounds.width / 2),
+						Math.floor(y - b.bounds.height / 2), b.bounds.width, b.bounds.height);
+				b.redraw();
+				mxUtils.setOpacity(b.node, this.virtualBendOpacity);
+				last = pt;
+				
+				if (this.manageLabelHandle)
+				{
+					this.checkLabelHandle(b.bounds);
+				}
+			}
+		}
+	}
+	
+	if (this.labelShape != null)
+	{
+		this.labelShape.redraw();
+	}
+	
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			this.customHandles[i].redraw();
+		}
+	}
+};
+
+/**
+ * Function: hideHandles
+ * 
+ * Shortcut to <hideSizers>.
+ */
+mxEdgeHandler.prototype.setHandlesVisible = function(visible)
+{
+	if (this.bends != null)
+	{
+		for (var i = 0; i < this.bends.length; i++)
+		{
+			this.bends[i].node.style.display = (visible) ? '' : 'none';
+		}
+	}
+	
+	if (this.virtualBends != null)
+	{
+		for (var i = 0; i < this.virtualBends.length; i++)
+		{
+			this.virtualBends[i].node.style.display = (visible) ? '' : 'none';
+		}
+	}
+
+	if (this.labelShape != null)
+	{
+		this.labelShape.node.style.display = (visible) ? '' : 'none';
+	}
+	
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			this.customHandles[i].setVisible(visible);
+		}
+	}
+};
+
+/**
+ * Function: redrawInnerBends
+ * 
+ * Updates and redraws the inner bends.
+ * 
+ * Parameters:
+ * 
+ * p0 - <mxPoint> that represents the location of the first point.
+ * pe - <mxPoint> that represents the location of the last point.
+ */
+mxEdgeHandler.prototype.redrawInnerBends = function(p0, pe)
+{
+	for (var i = 1; i < this.bends.length - 1; i++)
+	{
+		if (this.bends[i] != null)
+		{
+			if (this.abspoints[i] != null)
+			{
+				var x = this.abspoints[i].x;
+				var y = this.abspoints[i].y;
+				
+				var b = this.bends[i].bounds;
+				this.bends[i].node.style.visibility = 'visible';
+				this.bends[i].bounds = new mxRectangle(Math.round(x - b.width / 2),
+						Math.round(y - b.height / 2), b.width, b.height);
+				
+				if (this.manageLabelHandle)
+				{
+					this.checkLabelHandle(this.bends[i].bounds);
+				}
+				else if (this.handleImage == null && this.labelShape.visible && mxUtils.intersects(this.bends[i].bounds, this.labelShape.bounds))
+				{
+					w = mxConstants.HANDLE_SIZE + 3;
+					h = mxConstants.HANDLE_SIZE + 3;
+					this.bends[i].bounds = new mxRectangle(Math.round(x - w / 2), Math.round(y - h / 2), w, h);
+				}
+				
+				this.bends[i].redraw();
+			}
+			else
+			{
+				this.bends[i].destroy();
+				this.bends[i] = null;
+			}
+		}
+	}
+};
+
+/**
+ * Function: checkLabelHandle
+ * 
+ * Checks if the label handle intersects the given bounds and moves it if it
+ * intersects.
+ */
+mxEdgeHandler.prototype.checkLabelHandle = function(b)
+{
+	if (this.labelShape != null)
+	{
+		var b2 = this.labelShape.bounds;
+		
+		if (mxUtils.intersects(b, b2))
+		{
+			if (b.getCenterY() < b2.getCenterY())
+			{
+				b2.y = b.y + b.height;
+			}
+			else
+			{
+				b2.y = b.y - b2.height;
+			}
+		}
+	}
+};
+
+/**
+ * Function: drawPreview
+ * 
+ * Redraws the preview.
+ */
+mxEdgeHandler.prototype.drawPreview = function()
+{
+	if (this.isLabel)
+	{
+		var b = this.labelShape.bounds;
+		var bounds = new mxRectangle(Math.round(this.label.x - b.width / 2),
+				Math.round(this.label.y - b.height / 2), b.width, b.height);
+		this.labelShape.bounds = bounds;
+		this.labelShape.redraw();
+	}
+	else if (this.shape != null)
+	{
+		this.shape.apply(this.state);
+		this.shape.points = this.abspoints;
+		this.shape.scale = this.state.view.scale;
+		this.shape.isDashed = this.isSelectionDashed();
+		this.shape.stroke = this.getSelectionColor();
+		this.shape.strokewidth = this.getSelectionStrokeWidth() / this.shape.scale / this.shape.scale;
+		this.shape.isShadow = false;
+		this.shape.redraw();
+	}
+	
+	if (this.parentHighlight != null)
+	{
+		this.parentHighlight.redraw();
+	}
+};
+
+/**
+ * Function: refresh
+ * 
+ * Refreshes the bends of this handler.
+ */
+mxEdgeHandler.prototype.refresh = function()
+{
+	this.abspoints = this.getSelectionPoints(this.state);
+	this.points = [];
+
+	if (this.shape != null)
+	{
+		this.shape.points = this.abspoints;
+	}
+	
+	if (this.bends != null)
+	{
+		this.destroyBends(this.bends);
+		this.bends = this.createBends();
+	}
+	
+	if (this.virtualBends != null)
+	{
+		this.destroyBends(this.virtualBends);
+		this.virtualBends = this.createVirtualBends();
+	}
+	
+	if (this.customHandles != null)
+	{
+		this.destroyBends(this.customHandles);
+		this.customHandles = this.createCustomHandles();
+	}
+	
+	// Puts label node on top of bends
+	if (this.labelShape != null && this.labelShape.node != null && this.labelShape.node.parentNode != null)
+	{
+		this.labelShape.node.parentNode.appendChild(this.labelShape.node);
+	}
+};
+
+/**
+ * Function: destroyBends
+ * 
+ * Destroys all elements in <bends>.
+ */
+mxEdgeHandler.prototype.destroyBends = function(bends)
+{
+	if (bends != null)
+	{
+		for (var i = 0; i < bends.length; i++)
+		{
+			if (bends[i] != null)
+			{
+				bends[i].destroy();
+			}
+		}
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes. This does
+ * normally not need to be called as handlers are destroyed automatically
+ * when the corresponding cell is deselected.
+ */
+mxEdgeHandler.prototype.destroy = function()
+{
+	if (this.escapeHandler != null)
+	{
+		this.state.view.graph.removeListener(this.escapeHandler);
+		this.escapeHandler = null;
+	}
+	
+	if (this.marker != null)
+	{
+		this.marker.destroy();
+		this.marker = null;
+	}
+	
+	if (this.shape != null)
+	{
+		this.shape.destroy();
+		this.shape = null;
+	}
+	
+	if (this.parentHighlight != null)
+	{
+		this.parentHighlight.destroy();
+		this.parentHighlight = null;
+	}
+	
+	if (this.labelShape != null)
+	{
+		this.labelShape.destroy();
+		this.labelShape = null;
+	}
+
+	if (this.constraintHandler != null)
+	{
+		this.constraintHandler.destroy();
+		this.constraintHandler = null;
+	}
+	
+	this.destroyBends(this.virtualBends);
+	this.virtualBends = null;
+	
+	this.destroyBends(this.customHandles);
+	this.customHandles = null;
+
+	this.destroyBends(this.bends);
+	this.bends = null;
+	
+	this.removeHint();
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/handler/mxEdgeSegmentHandler.js b/airavata-kubernetes/web-console/src/assets/js/handler/mxEdgeSegmentHandler.js
new file mode 100644
index 0000000..513344e
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/handler/mxEdgeSegmentHandler.js
@@ -0,0 +1,401 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+function mxEdgeSegmentHandler(state)
+{
+	mxEdgeHandler.call(this, state);
+};
+
+/**
+ * Extends mxEdgeHandler.
+ */
+mxUtils.extend(mxEdgeSegmentHandler, mxElbowEdgeHandler);
+
+/**
+ * Function: getCurrentPoints
+ * 
+ * Returns the current absolute points.
+ */
+mxEdgeSegmentHandler.prototype.getCurrentPoints = function()
+{
+	var pts = this.state.absolutePoints;
+	
+	if (pts != null)
+	{
+		// Special case for straight edges where we add a virtual middle handle for moving the edge
+		if (pts.length == 2 || (pts.length == 3 && (pts[0].x == pts[1].x && pts[1].x == pts[2].x ||
+				pts[0].y == pts[1].y && pts[1].y == pts[2].y)))
+		{
+			var cx = pts[0].x + (pts[pts.length - 1].x - pts[0].x) / 2;
+			var cy = pts[0].y + (pts[pts.length - 1].y - pts[0].y) / 2;
+			
+			pts = [pts[0], new mxPoint(cx, cy), new mxPoint(cx, cy), pts[pts.length - 1]];	
+		}
+	}
+
+	return pts;
+};
+
+/**
+ * Function: getPreviewPoints
+ * 
+ * Updates the given preview state taking into account the state of the constraint handler.
+ */
+mxEdgeSegmentHandler.prototype.getPreviewPoints = function(point)
+{
+	if (this.isSource || this.isTarget)
+	{
+		return mxElbowEdgeHandler.prototype.getPreviewPoints.apply(this, arguments);
+	}
+	else
+	{
+		var pts = this.getCurrentPoints();
+		var last = this.convertPoint(pts[0].clone(), false);
+		point = this.convertPoint(point.clone(), false);
+		var result = [];
+
+		for (var i = 1; i < pts.length; i++)
+		{
+			var pt = this.convertPoint(pts[i].clone(), false);
+			
+			if (i == this.index)
+			{
+				if (Math.round(last.x - pt.x) == 0)
+		 		{
+					last.x = point.x;
+					pt.x = point.x;
+		 		}
+		 		
+				if (Math.round(last.y - pt.y) == 0)
+		 		{
+		 			last.y = point.y;
+		 			pt.y = point.y;
+		 		}
+			}
+
+			if (i < pts.length - 1)
+			{
+				result.push(pt);
+			}
+
+			last = pt;
+		}
+		
+		// Replaces single point that intersects with source or target
+		if (result.length == 1)
+		{
+			var source = this.state.getVisibleTerminalState(true);
+			var target = this.state.getVisibleTerminalState(false);
+			var scale = this.state.view.getScale();
+			var tr = this.state.view.getTranslate();
+			
+			var x = result[0].x * scale + tr.x;
+			var y = result[0].y * scale + tr.y;
+			
+			if ((source != null && mxUtils.contains(source, x, y)) ||
+				(target != null && mxUtils.contains(target, x, y)))
+			{
+				result = [point, point];
+			}
+		}
+
+		return result;
+	}
+};
+
+/**
+ * Function: updatePreviewState
+ * 
+ * Overridden to perform optimization of the edge style result.
+ */
+mxEdgeSegmentHandler.prototype.updatePreviewState = function(edge, point, terminalState, me)
+{
+	mxEdgeHandler.prototype.updatePreviewState.apply(this, arguments);
+
+	// Checks and corrects preview by running edge style again
+	if (!this.isSource && !this.isTarget)
+	{
+		point = this.convertPoint(point.clone(), false);
+		var pts = edge.absolutePoints;
+		var pt0 = pts[0];
+		var pt1 = pts[1];
+
+		var result = [];
+		
+		for (var i = 2; i < pts.length; i++)
+		{
+			var pt2 = pts[i];
+		
+			// Merges adjacent segments only if more than 2 to allow for straight edges
+			if ((Math.round(pt0.x - pt1.x) != 0 || Math.round(pt1.x - pt2.x) != 0) &&
+				(Math.round(pt0.y - pt1.y) != 0 || Math.round(pt1.y - pt2.y) != 0))
+			{
+				result.push(this.convertPoint(pt1.clone(), false));
+			}
+
+			pt0 = pt1;
+			pt1 = pt2;
+		}
+		
+		var source = this.state.getVisibleTerminalState(true);
+		var target = this.state.getVisibleTerminalState(false);
+		var rpts = this.state.absolutePoints;
+		
+		// A straight line is represented by 3 handles
+		if (result.length == 0 && (Math.round(pts[0].x - pts[pts.length - 1].x) == 0 ||
+			Math.round(pts[0].y - pts[pts.length - 1].y) == 0))
+		{
+			result = [point, point];
+		}
+		// Handles special case of transitions from straight vertical to routed
+		else if (pts.length == 5 && result.length == 2 && source != null && target != null &&
+				rpts != null && Math.round(rpts[0].x - rpts[rpts.length - 1].x) == 0)
+		{
+			var view = this.graph.getView();
+			var scale = view.getScale();
+			var tr = view.getTranslate();
+			
+			var y0 = view.getRoutingCenterY(source) / scale - tr.y;
+			
+			// Use fixed connection point y-coordinate if one exists
+			var sc = this.graph.getConnectionConstraint(edge, source, true);
+			
+			if (sc != null)
+			{
+				var pt = this.graph.getConnectionPoint(source, sc);
+				
+				if (pt != null)
+				{
+					this.convertPoint(pt, false);
+					y0 = pt.y;
+				}
+			}
+			
+			var ye = view.getRoutingCenterY(target) / scale - tr.y;
+			
+			// Use fixed connection point y-coordinate if one exists
+			var tc = this.graph.getConnectionConstraint(edge, target, false);
+			
+			if (tc)
+			{
+				var pt = this.graph.getConnectionPoint(target, tc);
+				
+				if (pt != null)
+				{
+					this.convertPoint(pt, false);
+					ye = pt.y;
+				}
+			}
+			
+			result = [new mxPoint(point.x, y0), new mxPoint(point.x, ye)];
+		}
+
+		this.points = result;
+
+		// LATER: Check if points and result are different
+		edge.view.updateFixedTerminalPoints(edge, source, target);
+		edge.view.updatePoints(edge, this.points, source, target);
+		edge.view.updateFloatingTerminalPoints(edge, source, target);
+	}
+};
+
+/**
+ * Overriden to merge edge segments.
+ */
+mxEdgeSegmentHandler.prototype.connect = function(edge, terminal, isSource, isClone, me)
+{
+	// Merges adjacent edge segments
+	var pts = this.abspoints;
+	var pt0 = pts[0];
+	var pt1 = pts[1];
+	var result = [];
+	
+	for (var i = 2; i < pts.length; i++)
+	{
+		var pt2 = pts[i];
+	
+		// Merges adjacent segments only if more than 2 to allow for straight edges
+		if ((Math.round(pt0.x - pt1.x) != 0 || Math.round(pt1.x - pt2.x) != 0) &&
+			(Math.round(pt0.y - pt1.y) != 0 || Math.round(pt1.y - pt2.y) != 0))
+		{
+			result.push(this.convertPoint(pt1.clone(), false));
+		}
+
+		pt0 = pt1;
+		pt1 = pt2;
+	}
+	
+	var model = this.graph.getModel();
+	
+	model.beginUpdate();
+	try
+	{
+		var geo = model.getGeometry(edge);
+		
+		if (geo != null)
+		{
+			geo = geo.clone();
+			geo.points = result;
+			
+			model.setGeometry(edge, geo);
+		}
+		
+		edge = mxEdgeHandler.prototype.connect.apply(this, arguments);
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+	
+	return edge;
+};
+
+/**
+ * Function: getTooltipForNode
+ * 
+ * Returns no tooltips.
+ */
+mxEdgeSegmentHandler.prototype.getTooltipForNode = function(node)
+{
+	return null;
+};
+
+/**
+ * Function: createBends
+ * 
+ * Adds custom bends for the center of each segment.
+ */
+mxEdgeSegmentHandler.prototype.start = function(x, y, index)
+{
+	mxEdgeHandler.prototype.start.apply(this, arguments);
+	
+	if (this.bends[index] != null && !this.isSource && !this.isTarget)
+	{
+		mxUtils.setOpacity(this.bends[index].node, 100);
+	}
+};
+
+/**
+ * Function: createBends
+ * 
+ * Adds custom bends for the center of each segment.
+ */
+mxEdgeSegmentHandler.prototype.createBends = function()
+{
+	var bends = [];
+	
+	// Source
+	var bend = this.createHandleShape(0);
+	this.initBend(bend);
+	bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);
+	bends.push(bend);
+
+	var pts = this.getCurrentPoints();
+
+	// Waypoints (segment handles)
+	if (this.graph.isCellBendable(this.state.cell))
+	{
+		if (this.points == null)
+		{
+			this.points = [];
+		}
+
+		for (var i = 0; i < pts.length - 1; i++)
+		{
+			bend = this.createVirtualBend();
+			bends.push(bend);
+			var horizontal = Math.round(pts[i].x - pts[i + 1].x) == 0;
+			
+			// Special case where dy is 0 as well
+			if (Math.round(pts[i].y - pts[i + 1].y) == 0 && i < pts.length - 2)
+			{
+				horizontal = Math.round(pts[i].x - pts[i + 2].x) == 0;
+			}
+			
+			bend.setCursor((horizontal) ? 'col-resize' : 'row-resize');
+			this.points.push(new mxPoint(0,0));
+		}
+	}
+
+	// Target
+	var bend = this.createHandleShape(pts.length);
+	this.initBend(bend);
+	bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);
+	bends.push(bend);
+
+	return bends;
+};
+
+/**
+ * Function: redraw
+ * 
+ * Overridden to invoke <refresh> before the redraw.
+ */
+mxEdgeSegmentHandler.prototype.redraw = function()
+{
+	this.refresh();
+	mxEdgeHandler.prototype.redraw.apply(this, arguments);
+};
+
+/**
+ * Function: redrawInnerBends
+ * 
+ * Updates the position of the custom bends.
+ */
+mxEdgeSegmentHandler.prototype.redrawInnerBends = function(p0, pe)
+{
+	if (this.graph.isCellBendable(this.state.cell))
+	{
+		var pts = this.getCurrentPoints();
+		
+		if (pts != null && pts.length > 1)
+		{
+			var straight = false;
+			
+			// Puts handle in the center of straight edges
+			if (pts.length == 4 && Math.round(pts[1].x - pts[2].x) == 0 && Math.round(pts[1].y - pts[2].y) == 0)
+			{
+				straight = true;
+				
+				if (Math.round(pts[0].y - pts[pts.length - 1].y) == 0)
+				{
+					var cx = pts[0].x + (pts[pts.length - 1].x - pts[0].x) / 2;
+					pts[1] = new mxPoint(cx, pts[1].y);
+					pts[2] = new mxPoint(cx, pts[2].y);
+				}
+				else
+				{
+					var cy = pts[0].y + (pts[pts.length - 1].y - pts[0].y) / 2;
+					pts[1] = new mxPoint(pts[1].x, cy);
+					pts[2] = new mxPoint(pts[2].x, cy);
+				}
+			}
+			
+			for (var i = 0; i < pts.length - 1; i++)
+			{
+				if (this.bends[i + 1] != null)
+				{
+		 			var p0 = pts[i];
+	 				var pe = pts[i + 1];
+			 		var pt = new mxPoint(p0.x + (pe.x - p0.x) / 2, p0.y + (pe.y - p0.y) / 2);
+			 		var b = this.bends[i + 1].bounds;
+			 		this.bends[i + 1].bounds = new mxRectangle(Math.floor(pt.x - b.width / 2),
+			 				Math.floor(pt.y - b.height / 2), b.width, b.height);
+				 	this.bends[i + 1].redraw();
+				 	
+				 	if (this.manageLabelHandle)
+					{
+						this.checkLabelHandle(this.bends[i + 1].bounds);
+					}
+				}
+			}
+			
+			if (straight)
+			{
+				mxUtils.setOpacity(this.bends[1].node, this.virtualBendOpacity);
+				mxUtils.setOpacity(this.bends[3].node, this.virtualBendOpacity);
+			}
+		}
+	}
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/handler/mxElbowEdgeHandler.js b/airavata-kubernetes/web-console/src/assets/js/handler/mxElbowEdgeHandler.js
new file mode 100644
index 0000000..e408f04
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/handler/mxElbowEdgeHandler.js
@@ -0,0 +1,229 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxElbowEdgeHandler
+ *
+ * Graph event handler that reconnects edges and modifies control points and
+ * the edge label location. Uses <mxTerminalMarker> for finding and
+ * highlighting new source and target vertices. This handler is automatically
+ * created in <mxGraph.createHandler>. It extends <mxEdgeHandler>.
+ * 
+ * Constructor: mxEdgeHandler
+ *
+ * Constructs an edge handler for the specified <mxCellState>.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> of the cell to be modified.
+ */
+function mxElbowEdgeHandler(state)
+{
+	mxEdgeHandler.call(this, state);
+};
+
+/**
+ * Extends mxEdgeHandler.
+ */
+mxUtils.extend(mxElbowEdgeHandler, mxEdgeHandler);
+
+/**
+ * Specifies if a double click on the middle handle should call
+ * <mxGraph.flipEdge>. Default is true.
+ */
+mxElbowEdgeHandler.prototype.flipEnabled = true;
+
+/**
+ * Variable: doubleClickOrientationResource
+ * 
+ * Specifies the resource key for the tooltip to be displayed on the single
+ * control point for routed edges. If the resource for this key does not
+ * exist then the value is used as the error message. Default is
+ * 'doubleClickOrientation'.
+ */
+mxElbowEdgeHandler.prototype.doubleClickOrientationResource =
+	(mxClient.language != 'none') ? 'doubleClickOrientation' : '';
+
+/**
+ * Function: createBends
+ * 
+ * Overrides <mxEdgeHandler.createBends> to create custom bends.
+ */
+ mxElbowEdgeHandler.prototype.createBends = function()
+ {
+	var bends = [];
+	
+	// Source
+	var bend = this.createHandleShape(0);
+	this.initBend(bend);
+	bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);
+	bends.push(bend);
+
+	// Virtual
+	bends.push(this.createVirtualBend(mxUtils.bind(this, function(evt)
+	{
+		if (!mxEvent.isConsumed(evt) && this.flipEnabled)
+		{
+			this.graph.flipEdge(this.state.cell, evt);
+			mxEvent.consume(evt);
+		}
+	})));
+	this.points.push(new mxPoint(0,0));
+
+	// Target
+	bend = this.createHandleShape(2);
+	this.initBend(bend);
+	bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);
+	bends.push(bend);
+	
+	return bends;
+ };
+
+/**
+ * Function: createVirtualBend
+ * 
+ * Creates a virtual bend that supports double clicking and calls
+ * <mxGraph.flipEdge>.
+ */
+mxElbowEdgeHandler.prototype.createVirtualBend = function(dblClickHandler)
+{
+	var bend = this.createHandleShape();
+	this.initBend(bend, dblClickHandler);
+
+	bend.setCursor(this.getCursorForBend());
+
+	if (!this.graph.isCellBendable(this.state.cell))
+	{
+		bend.node.style.display = 'none';
+	}
+
+	return bend;
+};
+
+/**
+ * Function: getCursorForBend
+ * 
+ * Returns the cursor to be used for the bend.
+ */
+mxElbowEdgeHandler.prototype.getCursorForBend = function()
+{
+	return (this.state.style[mxConstants.STYLE_EDGE] == mxEdgeStyle.TopToBottom ||
+		this.state.style[mxConstants.STYLE_EDGE] == mxConstants.EDGESTYLE_TOPTOBOTTOM ||
+		((this.state.style[mxConstants.STYLE_EDGE] == mxEdgeStyle.ElbowConnector ||
+		this.state.style[mxConstants.STYLE_EDGE] == mxConstants.EDGESTYLE_ELBOW)&&
+		this.state.style[mxConstants.STYLE_ELBOW] == mxConstants.ELBOW_VERTICAL)) ? 
+		'row-resize' : 'col-resize';
+};
+
+/**
+ * Function: getTooltipForNode
+ * 
+ * Returns the tooltip for the given node.
+ */
+mxElbowEdgeHandler.prototype.getTooltipForNode = function(node)
+{
+	var tip = null;
+	
+	if (this.bends != null && this.bends[1] != null && (node == this.bends[1].node ||
+		node.parentNode == this.bends[1].node))
+	{
+		tip = this.doubleClickOrientationResource;
+		tip = mxResources.get(tip) || tip; // translate
+	}
+
+	return tip;
+};
+
+/**
+ * Function: convertPoint
+ * 
+ * Converts the given point in-place from screen to unscaled, untranslated
+ * graph coordinates and applies the grid.
+ * 
+ * Parameters:
+ * 
+ * point - <mxPoint> to be converted.
+ * gridEnabled - Boolean that specifies if the grid should be applied.
+ */
+mxElbowEdgeHandler.prototype.convertPoint = function(point, gridEnabled)
+{
+	var scale = this.graph.getView().getScale();
+	var tr = this.graph.getView().getTranslate();
+	var origin = this.state.origin;
+	
+	if (gridEnabled)
+	{
+		point.x = this.graph.snap(point.x);
+		point.y = this.graph.snap(point.y);
+	}
+	
+	point.x = Math.round(point.x / scale - tr.x - origin.x);
+	point.y = Math.round(point.y / scale - tr.y - origin.y);
+	
+	return point;
+};
+
+/**
+ * Function: redrawInnerBends
+ * 
+ * Updates and redraws the inner bends.
+ * 
+ * Parameters:
+ * 
+ * p0 - <mxPoint> that represents the location of the first point.
+ * pe - <mxPoint> that represents the location of the last point.
+ */
+mxElbowEdgeHandler.prototype.redrawInnerBends = function(p0, pe)
+{
+	var g = this.graph.getModel().getGeometry(this.state.cell);
+	var pts = this.state.absolutePoints;
+	var pt = null;
+
+	// Keeps the virtual bend on the edge shape
+	if (pts.length > 1)
+	{
+		p0 = pts[1];
+		pe = pts[pts.length - 2];
+	}
+	else if (g.points != null && g.points.length > 0)
+	{
+		pt = pts[0];
+	}
+	
+	if (pt == null)
+	{
+		pt = new mxPoint(p0.x + (pe.x - p0.x) / 2, p0.y + (pe.y - p0.y) / 2);
+	}
+	else
+	{
+		pt = new mxPoint(this.graph.getView().scale * (pt.x + this.graph.getView().translate.x + this.state.origin.x),
+				this.graph.getView().scale * (pt.y + this.graph.getView().translate.y + this.state.origin.y));
+	}
+
+	// Makes handle slightly bigger if the yellow  label handle
+	// exists and intersects this green handle
+	var b = this.bends[1].bounds;
+	var w = b.width;
+	var h = b.height;
+	var bounds = new mxRectangle(Math.round(pt.x - w / 2), Math.round(pt.y - h / 2), w, h);
+
+	if (this.manageLabelHandle)
+	{
+		this.checkLabelHandle(bounds);
+	}
+	else if (this.handleImage == null && this.labelShape.visible && mxUtils.intersects(bounds, this.labelShape.bounds))
+	{
+		w = mxConstants.HANDLE_SIZE + 3;
+		h = mxConstants.HANDLE_SIZE + 3;
+		bounds = new mxRectangle(Math.floor(pt.x - w / 2), Math.floor(pt.y - h / 2), w, h);
+	}
+
+	this.bends[1].bounds = bounds;
+	this.bends[1].redraw();
+	
+	if (this.manageLabelHandle)
+	{
+		this.checkLabelHandle(this.bends[1].bounds);
+	}
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/handler/mxGraphHandler.js b/airavata-kubernetes/web-console/src/assets/js/handler/mxGraphHandler.js
new file mode 100644
index 0000000..0619d67
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/handler/mxGraphHandler.js
@@ -0,0 +1,1074 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGraphHandler
+ * 
+ * Graph event handler that handles selection. Individual cells are handled
+ * separately using <mxVertexHandler> or one of the edge handlers. These
+ * handlers are created using <mxGraph.createHandler> in
+ * <mxGraphSelectionModel.cellAdded>.
+ * 
+ * To avoid the container to scroll a moved cell into view, set
+ * <scrollAfterMove> to false.
+ * 
+ * Constructor: mxGraphHandler
+ * 
+ * Constructs an event handler that creates handles for the
+ * selection cells.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ */
+function mxGraphHandler(graph)
+{
+	this.graph = graph;
+	this.graph.addMouseListener(this);
+	
+	// Repaints the handler after autoscroll
+	this.panHandler = mxUtils.bind(this, function()
+	{
+		this.updatePreviewShape();
+		this.updateHint();
+	});
+	
+	this.graph.addListener(mxEvent.PAN, this.panHandler);
+	
+	// Handles escape keystrokes
+	this.escapeHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		this.reset();
+	});
+	
+	this.graph.addListener(mxEvent.ESCAPE, this.escapeHandler);
+};
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxGraphHandler.prototype.graph = null;
+
+/**
+ * Variable: maxCells
+ * 
+ * Defines the maximum number of cells to paint subhandles
+ * for. Default is 50 for Firefox and 20 for IE. Set this
+ * to 0 if you want an unlimited number of handles to be
+ * displayed. This is only recommended if the number of
+ * cells in the graph is limited to a small number, eg.
+ * 500.
+ */
+mxGraphHandler.prototype.maxCells = (mxClient.IS_IE) ? 20 : 50;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxGraphHandler.prototype.enabled = true;
+
+/**
+ * Variable: highlightEnabled
+ * 
+ * Specifies if drop targets under the mouse should be enabled. Default is
+ * true.
+ */
+mxGraphHandler.prototype.highlightEnabled = true;
+
+/**
+ * Variable: cloneEnabled
+ * 
+ * Specifies if cloning by control-drag is enabled. Default is true.
+ */
+mxGraphHandler.prototype.cloneEnabled = true;
+
+/**
+ * Variable: moveEnabled
+ * 
+ * Specifies if moving is enabled. Default is true.
+ */
+mxGraphHandler.prototype.moveEnabled = true;
+
+/**
+ * Variable: guidesEnabled
+ * 
+ * Specifies if other cells should be used for snapping the right, center or
+ * left side of the current selection. Default is false.
+ */
+mxGraphHandler.prototype.guidesEnabled = false;
+
+/**
+ * Variable: guide
+ * 
+ * Holds the <mxGuide> instance that is used for alignment.
+ */
+mxGraphHandler.prototype.guide = null;
+
+/**
+ * Variable: currentDx
+ * 
+ * Stores the x-coordinate of the current mouse move.
+ */
+mxGraphHandler.prototype.currentDx = null;
+
+/**
+ * Variable: currentDy
+ * 
+ * Stores the y-coordinate of the current mouse move.
+ */
+mxGraphHandler.prototype.currentDy = null;
+
+/**
+ * Variable: updateCursor
+ * 
+ * Specifies if a move cursor should be shown if the mouse is over a movable
+ * cell. Default is true.
+ */
+mxGraphHandler.prototype.updateCursor = true;
+
+/**
+ * Variable: selectEnabled
+ * 
+ * Specifies if selecting is enabled. Default is true.
+ */
+mxGraphHandler.prototype.selectEnabled = true;
+
+/**
+ * Variable: removeCellsFromParent
+ * 
+ * Specifies if cells may be moved out of their parents. Default is true.
+ */
+mxGraphHandler.prototype.removeCellsFromParent = true;
+
+/**
+ * Variable: connectOnDrop
+ * 
+ * Specifies if drop events are interpreted as new connections if no other
+ * drop action is defined. Default is false.
+ */
+mxGraphHandler.prototype.connectOnDrop = false;
+
+/**
+ * Variable: scrollOnMove
+ * 
+ * Specifies if the view should be scrolled so that a moved cell is
+ * visible. Default is true.
+ */
+mxGraphHandler.prototype.scrollOnMove = true;
+
+/**
+ * Variable: minimumSize
+ * 
+ * Specifies the minimum number of pixels for the width and height of a
+ * selection border. Default is 6.
+ */
+mxGraphHandler.prototype.minimumSize = 6;
+
+/**
+ * Variable: previewColor
+ * 
+ * Specifies the color of the preview shape. Default is black.
+ */
+mxGraphHandler.prototype.previewColor = 'black';
+
+/**
+ * Variable: htmlPreview
+ * 
+ * Specifies if the graph container should be used for preview. If this is used
+ * then drop target detection relies entirely on <mxGraph.getCellAt> because
+ * the HTML preview does not "let events through". Default is false.
+ */
+mxGraphHandler.prototype.htmlPreview = false;
+
+/**
+ * Variable: shape
+ * 
+ * Reference to the <mxShape> that represents the preview.
+ */
+mxGraphHandler.prototype.shape = null;
+
+/**
+ * Variable: scaleGrid
+ * 
+ * Specifies if the grid should be scaled. Default is false.
+ */
+mxGraphHandler.prototype.scaleGrid = false;
+
+/**
+ * Variable: rotationEnabled
+ * 
+ * Specifies if the bounding box should allow for rotation. Default is true.
+ */
+mxGraphHandler.prototype.rotationEnabled = true;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns <enabled>.
+ */
+mxGraphHandler.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ * 
+ * Sets <enabled>.
+ */
+mxGraphHandler.prototype.setEnabled = function(value)
+{
+	this.enabled = value;
+};
+
+/**
+ * Function: isCloneEnabled
+ * 
+ * Returns <cloneEnabled>.
+ */
+mxGraphHandler.prototype.isCloneEnabled = function()
+{
+	return this.cloneEnabled;
+};
+
+/**
+ * Function: setCloneEnabled
+ * 
+ * Sets <cloneEnabled>.
+ * 
+ * Parameters:
+ * 
+ * value - Boolean that specifies the new clone enabled state.
+ */
+mxGraphHandler.prototype.setCloneEnabled = function(value)
+{
+	this.cloneEnabled = value;
+};
+
+/**
+ * Function: isMoveEnabled
+ * 
+ * Returns <moveEnabled>.
+ */
+mxGraphHandler.prototype.isMoveEnabled = function()
+{
+	return this.moveEnabled;
+};
+
+/**
+ * Function: setMoveEnabled
+ * 
+ * Sets <moveEnabled>.
+ */
+mxGraphHandler.prototype.setMoveEnabled = function(value)
+{
+	this.moveEnabled = value;
+};
+
+/**
+ * Function: isSelectEnabled
+ * 
+ * Returns <selectEnabled>.
+ */
+mxGraphHandler.prototype.isSelectEnabled = function()
+{
+	return this.selectEnabled;
+};
+
+/**
+ * Function: setSelectEnabled
+ * 
+ * Sets <selectEnabled>.
+ */
+mxGraphHandler.prototype.setSelectEnabled = function(value)
+{
+	this.selectEnabled = value;
+};
+
+/**
+ * Function: isRemoveCellsFromParent
+ * 
+ * Returns <removeCellsFromParent>.
+ */
+mxGraphHandler.prototype.isRemoveCellsFromParent = function()
+{
+	return this.removeCellsFromParent;
+};
+
+/**
+ * Function: setRemoveCellsFromParent
+ * 
+ * Sets <removeCellsFromParent>.
+ */
+mxGraphHandler.prototype.setRemoveCellsFromParent = function(value)
+{
+	this.removeCellsFromParent = value;
+};
+
+/**
+ * Function: getInitialCellForEvent
+ * 
+ * Hook to return initial cell for the given event.
+ */
+mxGraphHandler.prototype.getInitialCellForEvent = function(me)
+{
+	return me.getCell();
+};
+
+/**
+ * Function: isDelayedSelection
+ * 
+ * Hook to return true for delayed selections.
+ */
+mxGraphHandler.prototype.isDelayedSelection = function(cell, me)
+{
+	return this.graph.isCellSelected(cell);
+};
+
+/**
+ * Function: consumeMouseEvent
+ * 
+ * Consumes the given mouse event. NOTE: This may be used to enable click
+ * events for links in labels on iOS as follows as consuming the initial
+ * touchStart disables firing the subsequent click evnent on the link.
+ * 
+ * <code>
+ * mxGraphHandler.prototype.consumeMouseEvent = function(evtName, me)
+ * {
+ *   var source = mxEvent.getSource(me.getEvent());
+ *   
+ *   if (!mxEvent.isTouchEvent(me.getEvent()) || source.nodeName != 'A')
+ *   {
+ *     me.consume();
+ *   }
+ * }
+ * </code>
+ */
+mxGraphHandler.prototype.consumeMouseEvent = function(evtName, me)
+{
+	me.consume();
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by selecing the given cell and creating a handle for
+ * it. By consuming the event all subsequent events of the gesture are
+ * redirected to this handler.
+ */
+mxGraphHandler.prototype.mouseDown = function(sender, me)
+{
+	if (!me.isConsumed() && this.isEnabled() && this.graph.isEnabled() &&
+		me.getState() != null && !mxEvent.isMultiTouchEvent(me.getEvent()))
+	{
+		var cell = this.getInitialCellForEvent(me);
+		this.delayedSelection = this.isDelayedSelection(cell, me);
+		this.cell = null;
+		
+		if (this.isSelectEnabled() && !this.delayedSelection)
+		{
+			this.graph.selectCellForEvent(cell, me.getEvent());
+		}
+
+		if (this.isMoveEnabled())
+		{
+			var model = this.graph.model;
+			var geo = model.getGeometry(cell);
+
+			if (this.graph.isCellMovable(cell) && ((!model.isEdge(cell) || this.graph.getSelectionCount() > 1 ||
+				(geo.points != null && geo.points.length > 0) || model.getTerminal(cell, true) == null ||
+				model.getTerminal(cell, false) == null) || this.graph.allowDanglingEdges || 
+				(this.graph.isCloneEvent(me.getEvent()) && this.graph.isCellsCloneable())))
+			{
+				this.start(cell, me.getX(), me.getY());
+			}
+			else if (this.delayedSelection)
+			{
+				this.cell = cell;
+			}
+
+			this.cellWasClicked = true;
+			this.consumeMouseEvent(mxEvent.MOUSE_DOWN, me);
+		}
+	}
+};
+
+/**
+ * Function: getGuideStates
+ * 
+ * Creates an array of cell states which should be used as guides.
+ */
+mxGraphHandler.prototype.getGuideStates = function()
+{
+	var parent = this.graph.getDefaultParent();
+	var model = this.graph.getModel();
+	
+	var filter = mxUtils.bind(this, function(cell)
+	{
+		return this.graph.view.getState(cell) != null &&
+			model.isVertex(cell) &&
+			model.getGeometry(cell) != null &&
+			!model.getGeometry(cell).relative;
+	});
+	
+	return this.graph.view.getCellStates(model.filterDescendants(filter, parent));
+};
+
+/**
+ * Function: getCells
+ * 
+ * Returns the cells to be modified by this handler. This implementation
+ * returns all selection cells that are movable, or the given initial cell if
+ * the given cell is not selected and movable. This handles the case of moving
+ * unselectable or unselected cells.
+ * 
+ * Parameters:
+ * 
+ * initialCell - <mxCell> that triggered this handler.
+ */
+mxGraphHandler.prototype.getCells = function(initialCell)
+{
+	if (!this.delayedSelection && this.graph.isCellMovable(initialCell))
+	{
+		return [initialCell];
+	}
+	else
+	{
+		return this.graph.getMovableCells(this.graph.getSelectionCells());
+	}
+};
+
+/**
+ * Function: getPreviewBounds
+ * 
+ * Returns the <mxRectangle> used as the preview bounds for
+ * moving the given cells.
+ */
+mxGraphHandler.prototype.getPreviewBounds = function(cells)
+{
+	var bounds = this.getBoundingBox(cells);
+	
+	if (bounds != null)
+	{
+		// Corrects width and height
+		bounds.width = Math.max(0, bounds.width - 1);
+		bounds.height = Math.max(0, bounds.height - 1);
+		
+		if (bounds.width < this.minimumSize)
+		{
+			var dx = this.minimumSize - bounds.width;
+			bounds.x -= dx / 2;
+			bounds.width = this.minimumSize;
+		}
+		else
+		{
+			bounds.x = Math.round(bounds.x);
+			bounds.width = Math.ceil(bounds.width);
+		}
+		
+		var tr = this.graph.view.translate;
+		var s = this.graph.view.scale;
+		
+		if (bounds.height < this.minimumSize)
+		{
+			var dy = this.minimumSize - bounds.height;
+			bounds.y -= dy / 2;
+			bounds.height = this.minimumSize;
+		}
+		else
+		{
+			bounds.y = Math.round(bounds.y);
+			bounds.height = Math.ceil(bounds.height);
+		}
+	}
+	
+	return bounds;
+};
+
+/**
+ * Function: getBoundingBox
+ * 
+ * Returns the union of the <mxCellStates> for the given array of <mxCells>.
+ * For vertices, this method uses the bounding box of the corresponding shape
+ * if one exists. The bounding box of the corresponding text label and all
+ * controls and overlays are ignored. See also: <mxGraphView.getBounds> and
+ * <mxGraph.getBoundingBox>.
+ *
+ * Parameters:
+ *
+ * cells - Array of <mxCells> whose bounding box should be returned.
+ */
+mxGraphHandler.prototype.getBoundingBox = function(cells)
+{
+	var result = null;
+	
+	if (cells != null && cells.length > 0)
+	{
+		var model = this.graph.getModel();
+		
+		for (var i = 0; i < cells.length; i++)
+		{
+			if (model.isVertex(cells[i]) || model.isEdge(cells[i]))
+			{
+				var state = this.graph.view.getState(cells[i]);
+			
+				if (state != null)
+				{
+					var bbox = state;
+					
+					if (model.isVertex(cells[i]) && state.shape != null && state.shape.boundingBox != null)
+					{
+						bbox = state.shape.boundingBox;
+					}
+					
+					if (result == null)
+					{
+						result = mxRectangle.fromRectangle(bbox);
+					}
+					else
+					{
+						result.add(bbox);
+					}
+				}
+			}
+		}
+	}
+	
+	return result;
+};
+
+/**
+ * Function: createPreviewShape
+ * 
+ * Creates the shape used to draw the preview for the given bounds.
+ */
+mxGraphHandler.prototype.createPreviewShape = function(bounds)
+{
+	var shape = new mxRectangleShape(bounds, null, this.previewColor);
+	shape.isDashed = true;
+	
+	if (this.htmlPreview)
+	{
+		shape.dialect = mxConstants.DIALECT_STRICTHTML;
+		shape.init(this.graph.container);
+	}
+	else
+	{
+		// Makes sure to use either VML or SVG shapes in order to implement
+		// event-transparency on the background area of the rectangle since
+		// HTML shapes do not let mouseevents through even when transparent
+		shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+			mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+		shape.init(this.graph.getView().getOverlayPane());
+		shape.pointerEvents = false;
+		
+		// Workaround for artifacts on iOS
+		if (mxClient.IS_IOS)
+		{
+			shape.getSvgScreenOffset = function()
+			{
+				return 0;
+			};
+		}
+	}
+	
+	return shape;
+};
+
+/**
+ * Function: start
+ * 
+ * Starts the handling of the mouse gesture.
+ */
+mxGraphHandler.prototype.start = function(cell, x, y)
+{
+	this.cell = cell;
+	this.first = mxUtils.convertPoint(this.graph.container, x, y);
+	this.cells = this.getCells(this.cell);
+	this.bounds = this.graph.getView().getBounds(this.cells);
+	this.pBounds = this.getPreviewBounds(this.cells);
+
+	if (this.guidesEnabled)
+	{
+		this.guide = new mxGuide(this.graph, this.getGuideStates());
+	}
+};
+
+/**
+ * Function: useGuidesForEvent
+ * 
+ * Returns true if the guides should be used for the given <mxMouseEvent>.
+ * This implementation returns <mxGuide.isEnabledForEvent>.
+ */
+mxGraphHandler.prototype.useGuidesForEvent = function(me)
+{
+	return (this.guide != null) ? this.guide.isEnabledForEvent(me.getEvent()) : true;
+};
+
+
+/**
+ * Function: snap
+ * 
+ * Snaps the given vector to the grid and returns the given mxPoint instance.
+ */
+mxGraphHandler.prototype.snap = function(vector)
+{
+	var scale = (this.scaleGrid) ? this.graph.view.scale : 1;
+	
+	vector.x = this.graph.snap(vector.x / scale) * scale;
+	vector.y = this.graph.snap(vector.y / scale) * scale;
+	
+	return vector;
+};
+
+/**
+ * Function: getDelta
+ * 
+ * Returns an <mxPoint> that represents the vector for moving the cells
+ * for the given <mxMouseEvent>.
+ */
+mxGraphHandler.prototype.getDelta = function(me)
+{
+	var point = mxUtils.convertPoint(this.graph.container, me.getX(), me.getY());
+	var s = this.graph.view.scale;
+	
+	return new mxPoint(this.roundLength((point.x - this.first.x) / s) * s,
+		this.roundLength((point.y - this.first.y) / s) * s);
+};
+
+/**
+ * Function: updateHint
+ * 
+ * Hook for subclassers do show details while the handler is active.
+ */
+mxGraphHandler.prototype.updateHint = function(me) { };
+
+/**
+ * Function: removeHint
+ * 
+ * Hooks for subclassers to hide details when the handler gets inactive.
+ */
+mxGraphHandler.prototype.removeHint = function() { };
+
+/**
+ * Function: roundLength
+ * 
+ * Hook for rounding the unscaled vector. This uses Math.round.
+ */
+mxGraphHandler.prototype.roundLength = function(length)
+{
+	return Math.round(length);
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by highlighting possible drop targets and updating the
+ * preview.
+ */
+mxGraphHandler.prototype.mouseMove = function(sender, me)
+{
+	var graph = this.graph;
+
+	if (!me.isConsumed() && graph.isMouseDown && this.cell != null &&
+		this.first != null && this.bounds != null)
+	{
+		// Stops moving if a multi touch event is received
+		if (mxEvent.isMultiTouchEvent(me.getEvent()))
+		{
+			this.reset();
+			return;
+		}
+		
+		var delta = this.getDelta(me);
+		var dx = delta.x;
+		var dy = delta.y;
+		var tol = graph.tolerance;
+
+		if (this.shape != null || Math.abs(dx) > tol || Math.abs(dy) > tol)
+		{
+			// Highlight is used for highlighting drop targets
+			if (this.highlight == null)
+			{
+				this.highlight = new mxCellHighlight(this.graph,
+					mxConstants.DROP_TARGET_COLOR, 3);
+			}
+			
+			if (this.shape == null)
+			{
+				this.shape = this.createPreviewShape(this.bounds);
+			}
+			
+			var gridEnabled = graph.isGridEnabledEvent(me.getEvent());
+			var hideGuide = true;
+			
+			if (this.guide != null && this.useGuidesForEvent(me))
+			{
+				delta = this.guide.move(this.bounds, new mxPoint(dx, dy), gridEnabled);
+				hideGuide = false;
+				dx = delta.x;
+				dy = delta.y;
+			}
+			else if (gridEnabled)
+			{
+				var trx = graph.getView().translate;
+				var scale = graph.getView().scale;				
+				
+				var tx = this.bounds.x - (graph.snap(this.bounds.x / scale - trx.x) + trx.x) * scale;
+				var ty = this.bounds.y - (graph.snap(this.bounds.y / scale - trx.y) + trx.y) * scale;
+				var v = this.snap(new mxPoint(dx, dy));
+			
+				dx = v.x - tx;
+				dy = v.y - ty;
+			}
+			
+			if (this.guide != null && hideGuide)
+			{
+				this.guide.hide();
+			}
+
+			// Constrained movement if shift key is pressed
+			if (graph.isConstrainedEvent(me.getEvent()))
+			{
+				if (Math.abs(dx) > Math.abs(dy))
+				{
+					dy = 0;
+				}
+				else
+				{
+					dx = 0;
+				}
+			}
+
+			this.currentDx = dx;
+			this.currentDy = dy;
+			this.updatePreviewShape();
+
+			var target = null;
+			var cell = me.getCell();
+
+			var clone = graph.isCloneEvent(me.getEvent()) && graph.isCellsCloneable() && this.isCloneEnabled();
+			
+			if (graph.isDropEnabled() && this.highlightEnabled)
+			{
+				// Contains a call to getCellAt to find the cell under the mouse
+				target = graph.getDropTarget(this.cells, me.getEvent(), cell, clone);
+			}
+
+			var state = graph.getView().getState(target);
+			var highlight = false;
+			
+			if (state != null && (graph.model.getParent(this.cell) != target || clone))
+			{
+			    if (this.target != target)
+			    {
+				    this.target = target;
+				    this.setHighlightColor(mxConstants.DROP_TARGET_COLOR);
+				}
+			    
+			    highlight = true;
+			}
+			else
+			{
+				this.target = null;
+
+				if (this.connectOnDrop && cell != null && this.cells.length == 1 &&
+					graph.getModel().isVertex(cell) && graph.isCellConnectable(cell))
+				{
+					state = graph.getView().getState(cell);
+					
+					if (state != null)
+					{
+						var error = graph.getEdgeValidationError(null, this.cell, cell);
+						var color = (error == null) ?
+							mxConstants.VALID_COLOR :
+							mxConstants.INVALID_CONNECT_TARGET_COLOR;
+						this.setHighlightColor(color);
+						highlight = true;
+					}
+				}
+			}
+			
+			if (state != null && highlight)
+			{
+				this.highlight.highlight(state);
+			}
+			else
+			{
+				this.highlight.hide();
+			}
+		}
+
+		this.updateHint(me);
+		this.consumeMouseEvent(mxEvent.MOUSE_MOVE, me);
+		
+		// Cancels the bubbling of events to the container so
+		// that the droptarget is not reset due to an mouseMove
+		// fired on the container with no associated state.
+		mxEvent.consume(me.getEvent());
+	}
+	else if ((this.isMoveEnabled() || this.isCloneEnabled()) && this.updateCursor &&
+		!me.isConsumed() && me.getState() != null && !graph.isMouseDown)
+	{
+		var cursor = graph.getCursorForMouseEvent(me);
+		
+		if (cursor == null && graph.isEnabled() && graph.isCellMovable(me.getCell()))
+		{
+			if (graph.getModel().isEdge(me.getCell()))
+			{
+				cursor = mxConstants.CURSOR_MOVABLE_EDGE;
+			}
+			else
+			{
+				cursor = mxConstants.CURSOR_MOVABLE_VERTEX;
+			}
+		}
+
+		// Sets the cursor on the original source state under the mouse
+		// instead of the event source state which can be the parent
+		if (me.sourceState != null)
+		{
+			me.sourceState.setCursor(cursor);
+		}
+	}
+};
+
+/**
+ * Function: updatePreviewShape
+ * 
+ * Updates the bounds of the preview shape.
+ */
+mxGraphHandler.prototype.updatePreviewShape = function()
+{
+	if (this.shape != null)
+	{
+		this.shape.bounds = new mxRectangle(Math.round(this.pBounds.x + this.currentDx - this.graph.panDx),
+				Math.round(this.pBounds.y + this.currentDy - this.graph.panDy), this.pBounds.width, this.pBounds.height);
+		this.shape.redraw();
+	}
+};
+
+/**
+ * Function: setHighlightColor
+ * 
+ * Sets the color of the rectangle used to highlight drop targets.
+ * 
+ * Parameters:
+ * 
+ * color - String that represents the new highlight color.
+ */
+mxGraphHandler.prototype.setHighlightColor = function(color)
+{
+	if (this.highlight != null)
+	{
+		this.highlight.setHighlightColor(color);
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by applying the changes to the selection cells.
+ */
+mxGraphHandler.prototype.mouseUp = function(sender, me)
+{
+	if (!me.isConsumed())
+	{
+		var graph = this.graph;
+		
+		if (this.cell != null && this.first != null && this.shape != null &&
+			this.currentDx != null && this.currentDy != null)
+		{
+			var cell = me.getCell();
+			
+			if (this.connectOnDrop && this.target == null && cell != null && graph.getModel().isVertex(cell) &&
+				graph.isCellConnectable(cell) && graph.isEdgeValid(null, this.cell, cell))
+			{
+				graph.connectionHandler.connect(this.cell, cell, me.getEvent());
+			}
+			else
+			{
+				var clone = graph.isCloneEvent(me.getEvent()) && graph.isCellsCloneable() && this.isCloneEnabled();
+				var scale = graph.getView().scale;
+				var dx = this.roundLength(this.currentDx / scale);
+				var dy = this.roundLength(this.currentDy / scale);
+				var target = this.target;
+				
+				if (graph.isSplitEnabled() && graph.isSplitTarget(target, this.cells, me.getEvent()))
+				{
+					graph.splitEdge(target, this.cells, null, dx, dy);
+				}
+				else
+				{
+					this.moveCells(this.cells, dx, dy, clone, this.target, me.getEvent());
+				}
+			}
+		}
+		else if (this.isSelectEnabled() && this.delayedSelection && this.cell != null)
+		{
+			this.selectDelayed(me);
+		}
+	}
+
+	// Consumes the event if a cell was initially clicked
+	if (this.cellWasClicked)
+	{
+		this.consumeMouseEvent(mxEvent.MOUSE_UP, me);
+	}
+
+	this.reset();
+};
+
+/**
+ * Function: selectDelayed
+ * 
+ * Implements the delayed selection for the given mouse event.
+ */
+mxGraphHandler.prototype.selectDelayed = function(me)
+{
+	if (!this.graph.isCellSelected(this.cell) || !this.graph.popupMenuHandler.isPopupTrigger(me))
+	{
+		this.graph.selectCellForEvent(this.cell, me.getEvent());
+	}
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of this handler.
+ */
+mxGraphHandler.prototype.reset = function()
+{
+	this.destroyShapes();
+	this.removeHint();
+	
+	this.cellWasClicked = false;
+	this.delayedSelection = false;
+	this.currentDx = null;
+	this.currentDy = null;
+	this.guides = null;
+	this.first = null;
+	this.cell = null;
+	this.target = null;
+};
+
+/**
+ * Function: shouldRemoveCellsFromParent
+ * 
+ * Returns true if the given cells should be removed from the parent for the specified
+ * mousereleased event.
+ */
+mxGraphHandler.prototype.shouldRemoveCellsFromParent = function(parent, cells, evt)
+{
+	if (this.graph.getModel().isVertex(parent))
+	{
+		var pState = this.graph.getView().getState(parent);
+		
+		if (pState != null)
+		{
+			var pt = mxUtils.convertPoint(this.graph.container,
+				mxEvent.getClientX(evt), mxEvent.getClientY(evt));
+			var alpha = mxUtils.toRadians(mxUtils.getValue(pState.style, mxConstants.STYLE_ROTATION) || 0);
+			
+			if (alpha != 0)
+			{
+				var cos = Math.cos(-alpha);
+				var sin = Math.sin(-alpha);
+				var cx = new mxPoint(pState.getCenterX(), pState.getCenterY());
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, cx);
+			}
+		
+			return !mxUtils.contains(pState, pt.x, pt.y);
+		}
+	}
+	
+	return false;
+};
+
+/**
+ * Function: moveCells
+ * 
+ * Moves the given cells by the specified amount.
+ */
+mxGraphHandler.prototype.moveCells = function(cells, dx, dy, clone, target, evt)
+{
+	if (clone)
+	{
+		cells = this.graph.getCloneableCells(cells);
+	}
+	
+	// Removes cells from parent
+	if (target == null && this.isRemoveCellsFromParent() &&
+		this.shouldRemoveCellsFromParent(this.graph.getModel().getParent(this.cell), cells, evt))
+	{
+		target = this.graph.getDefaultParent();
+	}
+	
+	// Passes all selected cells in order to correctly clone or move into
+	// the target cell. The method checks for each cell if its movable.
+	cells = this.graph.moveCells(cells, dx - this.graph.panDx / this.graph.view.scale,
+			dy - this.graph.panDy / this.graph.view.scale, clone, target, evt);
+	
+	if (this.isSelectEnabled() && this.scrollOnMove)
+	{
+		this.graph.scrollCellToVisible(cells[0]);
+	}
+			
+	// Selects the new cells if cells have been cloned
+	if (clone)
+	{
+		this.graph.setSelectionCells(cells);
+	}
+};
+
+/**
+ * Function: destroyShapes
+ * 
+ * Destroy the preview and highlight shapes.
+ */
+mxGraphHandler.prototype.destroyShapes = function()
+{
+	// Destroys the preview dashed rectangle
+	if (this.shape != null)
+	{
+		this.shape.destroy();
+		this.shape = null;
+	}
+	
+	if (this.guide != null)
+	{
+		this.guide.destroy();
+		this.guide = null;
+	}
+	
+	// Destroys the drop target highlight
+	if (this.highlight != null)
+	{
+		this.highlight.destroy();
+		this.highlight = null;
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxGraphHandler.prototype.destroy = function()
+{
+	this.graph.removeMouseListener(this);
+	this.graph.removeListener(this.panHandler);
+	
+	if (this.escapeHandler != null)
+	{
+		this.graph.removeListener(this.escapeHandler);
+		this.escapeHandler = null;
+	}
+	
+	this.destroyShapes();
+	this.removeHint();
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/handler/mxHandle.js b/airavata-kubernetes/web-console/src/assets/js/handler/mxHandle.js
new file mode 100644
index 0000000..5564925
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/handler/mxHandle.js
@@ -0,0 +1,353 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxHandle
+ * 
+ * Implements a single custom handle for vertices.
+ * 
+ * Constructor: mxHandle
+ * 
+ * Constructs a new handle for the given state.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> of the cell to be handled.
+ */
+function mxHandle(state, cursor, image)
+{
+	this.graph = state.view.graph;
+	this.state = state;
+	this.cursor = (cursor != null) ? cursor : this.cursor;
+	this.image = (image != null) ? image : this.image;
+	this.init();
+};
+
+/**
+ * Variable: cursor
+ * 
+ * Specifies the cursor to be used for this handle. Default is 'default'.
+ */
+mxHandle.prototype.cursor = 'default';
+
+/**
+ * Variable: image
+ * 
+ * Specifies the <mxImage> to be used to render the handle. Default is null.
+ */
+mxHandle.prototype.image = null;
+
+/**
+ * Variable: image
+ * 
+ * Specifies the <mxImage> to be used to render the handle. Default is null.
+ */
+mxHandle.prototype.ignoreGrid = false;
+
+/**
+ * Function: getPosition
+ * 
+ * Hook for subclassers to return the current position of the handle.
+ */
+mxHandle.prototype.getPosition = function(bounds) { };
+
+/**
+ * Function: setPosition
+ * 
+ * Hooks for subclassers to update the style in the <state>.
+ */
+mxHandle.prototype.setPosition = function(bounds, pt, me) { };
+
+/**
+ * Function: execute
+ * 
+ * Hook for subclassers to execute the handle.
+ */
+mxHandle.prototype.execute = function() { };
+
+/**
+ * Function: copyStyle
+ * 
+ * Sets the cell style with the given name to the corresponding value in <state>.
+ */
+mxHandle.prototype.copyStyle = function(key)
+{
+	this.graph.setCellStyles(key, this.state.style[key], [this.state.cell]);
+};
+
+/**
+ * Function: processEvent
+ * 
+ * Processes the given <mxMouseEvent> and invokes <setPosition>.
+ */
+mxHandle.prototype.processEvent = function(me)
+{
+	var scale = this.graph.view.scale;
+	var tr = this.graph.view.translate;
+	var pt = new mxPoint(me.getGraphX() / scale - tr.x, me.getGraphY() / scale - tr.y);
+	
+	// Center shape on mouse cursor
+	if (this.shape != null && this.shape.bounds != null)
+	{
+		pt.x -= this.shape.bounds.width / scale / 4;
+		pt.y -= this.shape.bounds.height / scale / 4;
+	}
+
+	// Snaps to grid for the rotated position then applies the rotation for the direction after that
+	var alpha1 = -mxUtils.toRadians(this.getRotation());
+	var alpha2 = -mxUtils.toRadians(this.getTotalRotation()) - alpha1;
+	pt = this.flipPoint(this.rotatePoint(this.snapPoint(this.rotatePoint(pt, alpha1),
+			this.ignoreGrid || !this.graph.isGridEnabledEvent(me.getEvent())), alpha2));
+	this.setPosition(this.state.getPaintBounds(), pt, me);
+	this.positionChanged();
+	this.redraw();
+};
+
+/**
+ * Function: positionChanged
+ * 
+ * Called after <setPosition> has been called in <processEvent>. This repaints
+ * the state using <mxCellRenderer>.
+ */
+mxHandle.prototype.positionChanged = function()
+{
+	if (this.state.text != null)
+	{
+		this.state.text.apply(this.state);
+	}
+	
+	if (this.state.shape != null)
+	{
+		this.state.shape.apply(this.state);
+	}
+	
+	// Needed to force update of text bounds
+	this.state.unscaledWidth = null;
+	this.graph.cellRenderer.redraw(this.state, true);
+};
+
+/**
+ * Function: getRotation
+ * 
+ * Returns the rotation defined in the style of the cell.
+ */
+mxHandle.prototype.getRotation = function()
+{
+	if (this.state.shape != null)
+	{
+		return this.state.shape.getRotation();
+	}
+	
+	return 0;
+};
+
+/**
+ * Function: getTotalRotation
+ * 
+ * Returns the rotation from the style and the rotation from the direction of
+ * the cell.
+ */
+mxHandle.prototype.getTotalRotation = function()
+{
+	if (this.state.shape != null)
+	{
+		return this.state.shape.getShapeRotation();
+	}
+	
+	return 0;
+};
+
+/**
+ * Function: init
+ * 
+ * Creates and initializes the shapes required for this handle.
+ */
+mxHandle.prototype.init = function()
+{
+	var html = this.isHtmlRequired();
+	
+	if (this.image != null)
+	{
+		this.shape = new mxImageShape(new mxRectangle(0, 0, this.image.width, this.image.height), this.image.src);
+		this.shape.preserveImageAspect = false;
+	}
+	else
+	{
+		this.shape = this.createShape(html);
+	}
+	
+	this.initShape(html);
+};
+
+/**
+ * Function: createShape
+ * 
+ * Creates and returns the shape for this handle.
+ */
+mxHandle.prototype.createShape = function(html)
+{
+	var bounds = new mxRectangle(0, 0, mxConstants.HANDLE_SIZE, mxConstants.HANDLE_SIZE);
+	
+	return new mxRectangleShape(bounds, mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
+};
+
+/**
+ * Function: initShape
+ * 
+ * Initializes <shape> and sets its cursor.
+ */
+mxHandle.prototype.initShape = function(html)
+{
+	if (html && this.shape.isHtmlAllowed())
+	{
+		this.shape.dialect = mxConstants.DIALECT_STRICTHTML;
+		this.shape.init(this.graph.container);
+	}
+	else
+	{
+		this.shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? mxConstants.DIALECT_MIXEDHTML : mxConstants.DIALECT_SVG;
+		
+		if (this.cursor != null)
+		{
+			this.shape.init(this.graph.getView().getOverlayPane());
+		}
+	}
+
+	mxEvent.redirectMouseEvents(this.shape.node, this.graph, this.state);
+	this.shape.node.style.cursor = this.cursor;
+};
+
+/**
+ * Function: redraw
+ * 
+ * Renders the shape for this handle.
+ */
+mxHandle.prototype.redraw = function()
+{
+	if (this.shape != null && this.state.shape != null)
+	{
+		var pt = this.getPosition(this.state.getPaintBounds());
+		
+		if (pt != null)
+		{
+			var alpha = mxUtils.toRadians(this.getTotalRotation());
+			pt = this.rotatePoint(this.flipPoint(pt), alpha);
+	
+			var scale = this.graph.view.scale;
+			var tr = this.graph.view.translate;
+			this.shape.bounds.x = Math.floor((pt.x + tr.x) * scale - this.shape.bounds.width / 2);
+			this.shape.bounds.y = Math.floor((pt.y + tr.y) * scale - this.shape.bounds.height / 2);
+			
+			// Needed to force update of text bounds
+			this.shape.redraw();
+		}
+	}
+};
+
+/**
+ * Function: isHtmlRequired
+ * 
+ * Returns true if this handle should be rendered in HTML. This returns true if
+ * the text node is in the graph container.
+ */
+mxHandle.prototype.isHtmlRequired = function()
+{
+	return this.state.text != null && this.state.text.node.parentNode == this.graph.container;
+};
+
+/**
+ * Function: rotatePoint
+ * 
+ * Rotates the point by the given angle.
+ */
+mxHandle.prototype.rotatePoint = function(pt, alpha)
+{
+	var bounds = this.state.getCellBounds();
+	var cx = new mxPoint(bounds.getCenterX(), bounds.getCenterY());
+	var cos = Math.cos(alpha);
+	var sin = Math.sin(alpha); 
+
+	return mxUtils.getRotatedPoint(pt, cos, sin, cx);
+};
+
+/**
+ * Function: flipPoint
+ * 
+ * Flips the given point vertically and/or horizontally.
+ */
+mxHandle.prototype.flipPoint = function(pt)
+{
+	if (this.state.shape != null)
+	{
+		var bounds = this.state.getCellBounds();
+		
+		if (this.state.shape.flipH)
+		{
+			pt.x = 2 * bounds.x + bounds.width - pt.x;
+		}
+		
+		if (this.state.shape.flipV)
+		{
+			pt.y = 2 * bounds.y + bounds.height - pt.y;
+		}
+	}
+	
+	return pt;
+};
+
+/**
+ * Function: snapPoint
+ * 
+ * Snaps the given point to the grid if ignore is false. This modifies
+ * the given point in-place and also returns it.
+ */
+mxHandle.prototype.snapPoint = function(pt, ignore)
+{
+	if (!ignore)
+	{
+		pt.x = this.graph.snap(pt.x);
+		pt.y = this.graph.snap(pt.y);
+	}
+	
+	return pt;
+};
+
+/**
+ * Function: setVisible
+ * 
+ * Shows or hides this handle.
+ */
+mxHandle.prototype.setVisible = function(visible)
+{
+	if (this.shape != null && this.shape.node != null)
+	{
+		this.shape.node.style.display = (visible) ? '' : 'none';
+	}
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of this handle by setting its visibility to true.
+ */
+mxHandle.prototype.reset = function()
+{
+	this.setVisible(true);
+	this.state.style = this.graph.getCellStyle(this.state.cell);
+	this.positionChanged();
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys this handle.
+ */
+mxHandle.prototype.destroy = function()
+{
+	if (this.shape != null)
+	{
+		this.shape.destroy();
+		this.shape = null;
+	}
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/handler/mxKeyHandler.js b/airavata-kubernetes/web-console/src/assets/js/handler/mxKeyHandler.js
new file mode 100644
index 0000000..6a391f0
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/handler/mxKeyHandler.js
@@ -0,0 +1,428 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxKeyHandler
+ *
+ * Event handler that listens to keystroke events. This is not a singleton,
+ * however, it is normally only required once if the target is the document
+ * element (default).
+ * 
+ * This handler installs a key event listener in the topmost DOM node and
+ * processes all events that originate from descandants of <mxGraph.container>
+ * or from the topmost DOM node. The latter means that all unhandled keystrokes
+ * are handled by this object regardless of the focused state of the <graph>.
+ * 
+ * Example:
+ * 
+ * The following example creates a key handler that listens to the delete key
+ * (46) and deletes the selection cells if the graph is enabled.
+ * 
+ * (code)
+ * var keyHandler = new mxKeyHandler(graph);
+ * keyHandler.bindKey(46, function(evt)
+ * {
+ *   if (graph.isEnabled())
+ *   {
+ *     graph.removeCells();
+ *   }
+ * });
+ * (end)
+ * 
+ * Keycodes:
+ * 
+ * See http://tinyurl.com/yp8jgl or http://tinyurl.com/229yqw for a list of
+ * keycodes or install a key event listener into the document element and print
+ * the key codes of the respective events to the console.
+ * 
+ * To support the Command key and the Control key on the Mac, the following
+ * code can be used.
+ *
+ * (code)
+ * keyHandler.getFunction = function(evt)
+ * {
+ *   if (evt != null)
+ *   {
+ *     return (mxEvent.isControlDown(evt) || (mxClient.IS_MAC && evt.metaKey)) ? this.controlKeys[evt.keyCode] : this.normalKeys[evt.keyCode];
+ *   }
+ *   
+ *   return null;
+ * };
+ * (end)
+ * 
+ * Constructor: mxKeyHandler
+ *
+ * Constructs an event handler that executes functions bound to specific
+ * keystrokes.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the associated <mxGraph>.
+ * target - Optional reference to the event target. If null, the document
+ * element is used as the event target, that is, the object where the key
+ * event listener is installed.
+ */
+function mxKeyHandler(graph, target)
+{
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.target = target || document.documentElement;
+		
+		// Creates the arrays to map from keycodes to functions
+		this.normalKeys = [];
+		this.shiftKeys = [];
+		this.controlKeys = [];
+		this.controlShiftKeys = [];
+		
+		this.keydownHandler = mxUtils.bind(this, function(evt)
+		{
+			this.keyDown(evt);
+		});
+
+		// Installs the keystroke listener in the target
+		mxEvent.addListener(this.target, 'keydown', this.keydownHandler);
+		
+		// Automatically deallocates memory in IE
+		if (mxClient.IS_IE)
+		{
+			mxEvent.addListener(window, 'unload',
+				mxUtils.bind(this, function()
+				{
+					this.destroy();
+				})
+			);
+		}
+	}
+};
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the <mxGraph> associated with this handler.
+ */
+mxKeyHandler.prototype.graph = null;
+
+/**
+ * Variable: target
+ * 
+ * Reference to the target DOM, that is, the DOM node where the key event
+ * listeners are installed.
+ */
+mxKeyHandler.prototype.target = null;
+
+/**
+ * Variable: normalKeys
+ * 
+ * Maps from keycodes to functions for non-pressed control keys.
+ */
+mxKeyHandler.prototype.normalKeys = null;
+
+/**
+ * Variable: shiftKeys
+ * 
+ * Maps from keycodes to functions for pressed shift keys.
+ */
+mxKeyHandler.prototype.shiftKeys = null;
+
+/**
+ * Variable: controlKeys
+ * 
+ * Maps from keycodes to functions for pressed control keys.
+ */
+mxKeyHandler.prototype.controlKeys = null;
+
+/**
+ * Variable: controlShiftKeys
+ * 
+ * Maps from keycodes to functions for pressed control and shift keys.
+ */
+mxKeyHandler.prototype.controlShiftKeys = null;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxKeyHandler.prototype.enabled = true;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation returns
+ * <enabled>.
+ */
+mxKeyHandler.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ * 
+ * Enables or disables event handling by updating <enabled>.
+ * 
+ * Parameters:
+ * 
+ * enabled - Boolean that specifies the new enabled state.
+ */
+mxKeyHandler.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: bindKey
+ * 
+ * Binds the specified keycode to the given function. This binding is used
+ * if the control key is not pressed.
+ * 
+ * Parameters:
+ *
+ * code - Integer that specifies the keycode.
+ * funct - JavaScript function that takes the key event as an argument.
+ */
+mxKeyHandler.prototype.bindKey = function(code, funct)
+{
+	this.normalKeys[code] = funct;
+};
+
+/**
+ * Function: bindShiftKey
+ * 
+ * Binds the specified keycode to the given function. This binding is used
+ * if the shift key is pressed.
+ * 
+ * Parameters:
+ *
+ * code - Integer that specifies the keycode.
+ * funct - JavaScript function that takes the key event as an argument.
+ */
+mxKeyHandler.prototype.bindShiftKey = function(code, funct)
+{
+	this.shiftKeys[code] = funct;
+};
+
+/**
+ * Function: bindControlKey
+ * 
+ * Binds the specified keycode to the given function. This binding is used
+ * if the control key is pressed.
+ * 
+ * Parameters:
+ *
+ * code - Integer that specifies the keycode.
+ * funct - JavaScript function that takes the key event as an argument.
+ */
+mxKeyHandler.prototype.bindControlKey = function(code, funct)
+{
+	this.controlKeys[code] = funct;
+};
+
+/**
+ * Function: bindControlShiftKey
+ * 
+ * Binds the specified keycode to the given function. This binding is used
+ * if the control and shift key are pressed.
+ * 
+ * Parameters:
+ *
+ * code - Integer that specifies the keycode.
+ * funct - JavaScript function that takes the key event as an argument.
+ */
+mxKeyHandler.prototype.bindControlShiftKey = function(code, funct)
+{
+	this.controlShiftKeys[code] = funct;
+};
+
+/**
+ * Function: isControlDown
+ * 
+ * Returns true if the control key is pressed. This uses <mxEvent.isControlDown>.
+ * 
+ * Parameters:
+ * 
+ * evt - Key event whose control key pressed state should be returned.
+ */
+mxKeyHandler.prototype.isControlDown = function(evt)
+{
+	return mxEvent.isControlDown(evt);
+};
+
+/**
+ * Function: getFunction
+ * 
+ * Returns the function associated with the given key event or null if no
+ * function is associated with the given event.
+ * 
+ * Parameters:
+ * 
+ * evt - Key event whose associated function should be returned.
+ */
+mxKeyHandler.prototype.getFunction = function(evt)
+{
+	if (evt != null && !mxEvent.isAltDown(evt))
+	{
+		if (this.isControlDown(evt))
+		{
+			if (mxEvent.isShiftDown(evt))
+			{
+				return this.controlShiftKeys[evt.keyCode];
+			}
+			else
+			{
+				return this.controlKeys[evt.keyCode];
+			}
+		}
+		else
+		{
+			if (mxEvent.isShiftDown(evt))
+			{
+				return this.shiftKeys[evt.keyCode];
+			}
+			else
+			{
+				return this.normalKeys[evt.keyCode];
+			}
+		}
+	}
+	
+	return null;
+};
+	
+/**
+ * Function: isGraphEvent
+ * 
+ * Returns true if the event should be processed by this handler, that is,
+ * if the event source is either the target, one of its direct children, a
+ * descendant of the <mxGraph.container>, or the <mxGraph.cellEditor> of the
+ * <graph>.
+ * 
+ * Parameters:
+ * 
+ * evt - Key event that represents the keystroke.
+ */
+mxKeyHandler.prototype.isGraphEvent = function(evt)
+{
+	var source = mxEvent.getSource(evt);
+	
+	// Accepts events from the target object or
+	// in-place editing inside graph
+	if ((source == this.target || source.parentNode == this.target) ||
+		(this.graph.cellEditor != null && this.graph.cellEditor.isEventSource(evt)))
+	{
+		return true;
+	}
+	
+	// Accepts events from inside the container
+	return mxUtils.isAncestorNode(this.graph.container, source);
+};
+
+/**
+ * Function: keyDown
+ * 
+ * Handles the event by invoking the function bound to the respective keystroke
+ * if <isEnabledForEvent> returns true for the given event and if
+ * <isEventIgnored> returns false, except for escape for which
+ * <isEventIgnored> is not invoked.
+ * 
+ * Parameters:
+ * 
+ * evt - Key event that represents the keystroke.
+ */
+mxKeyHandler.prototype.keyDown = function(evt)
+{
+	if (this.isEnabledForEvent(evt))
+	{
+		// Cancels the editing if escape is pressed
+		if (evt.keyCode == 27 /* Escape */)
+		{
+			this.escape(evt);
+		}
+		
+		// Invokes the function for the keystroke
+		else if (!this.isEventIgnored(evt))
+		{
+			var boundFunction = this.getFunction(evt);
+			
+			if (boundFunction != null)
+			{
+				boundFunction(evt);
+				mxEvent.consume(evt);
+			}
+		}
+	}
+};
+
+/**
+ * Function: isEnabledForEvent
+ * 
+ * Returns true if the given event should be handled. <isEventIgnored> is
+ * called later if the event is not an escape key stroke, in which case
+ * <escape> is called. This implementation returns true if <isEnabled>
+ * returns true for both, this handler and <graph>, if the event is not
+ * consumed and if <isGraphEvent> returns true.
+ * 
+ * Parameters:
+ * 
+ * evt - Key event that represents the keystroke.
+ */
+mxKeyHandler.prototype.isEnabledForEvent = function(evt)
+{
+	return (this.graph.isEnabled() && !mxEvent.isConsumed(evt) &&
+		this.isGraphEvent(evt) && this.isEnabled());
+};
+
+/**
+ * Function: isEventIgnored
+ * 
+ * Returns true if the given keystroke should be ignored. This returns
+ * graph.isEditing().
+ * 
+ * Parameters:
+ * 
+ * evt - Key event that represents the keystroke.
+ */
+mxKeyHandler.prototype.isEventIgnored = function(evt)
+{
+	return this.graph.isEditing();
+};
+
+/**
+ * Function: escape
+ * 
+ * Hook to process ESCAPE keystrokes. This implementation invokes
+ * <mxGraph.stopEditing> to cancel the current editing, connecting
+ * and/or other ongoing modifications.
+ * 
+ * Parameters:
+ * 
+ * evt - Key event that represents the keystroke. Possible keycode in this
+ * case is 27 (ESCAPE).
+ */
+mxKeyHandler.prototype.escape = function(evt)
+{
+	if (this.graph.isEscapeEnabled())
+	{
+		this.graph.escape(evt);
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its references into the DOM. This does
+ * normally not need to be called, it is called automatically when the
+ * window unloads (in IE).
+ */
+mxKeyHandler.prototype.destroy = function()
+{
+	if (this.target != null && this.keydownHandler != null)
+	{
+		mxEvent.removeListener(this.target, 'keydown', this.keydownHandler);
+		this.keydownHandler = null;
+	}
+	
+	this.target = null;
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/handler/mxPanningHandler.js b/airavata-kubernetes/web-console/src/assets/js/handler/mxPanningHandler.js
new file mode 100644
index 0000000..189e676
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/handler/mxPanningHandler.js
@@ -0,0 +1,462 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxPanningHandler
+ * 
+ * Event handler that pans and creates popupmenus. To use the left
+ * mousebutton for panning without interfering with cell moving and
+ * resizing, use <isUseLeftButton> and <isIgnoreCell>. For grid size
+ * steps while panning, use <useGrid>. This handler is built-into
+ * <mxGraph.panningHandler> and enabled using <mxGraph.setPanning>.
+ * 
+ * Constructor: mxPanningHandler
+ * 
+ * Constructs an event handler that creates a <mxPopupMenu>
+ * and pans the graph.
+ *
+ * Event: mxEvent.PAN_START
+ *
+ * Fires when the panning handler changes its <active> state to true. The
+ * <code>event</code> property contains the corresponding <mxMouseEvent>.
+ *
+ * Event: mxEvent.PAN
+ *
+ * Fires while handle is processing events. The <code>event</code> property contains
+ * the corresponding <mxMouseEvent>.
+ *
+ * Event: mxEvent.PAN_END
+ *
+ * Fires when the panning handler changes its <active> state to false. The
+ * <code>event</code> property contains the corresponding <mxMouseEvent>.
+ */
+function mxPanningHandler(graph)
+{
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.graph.addMouseListener(this);
+
+		// Handles force panning event
+		this.forcePanningHandler = mxUtils.bind(this, function(sender, evt)
+		{
+			var evtName = evt.getProperty('eventName');
+			var me = evt.getProperty('event');
+			
+			if (evtName == mxEvent.MOUSE_DOWN && this.isForcePanningEvent(me))
+			{
+				this.start(me);
+				this.active = true;
+				this.fireEvent(new mxEventObject(mxEvent.PAN_START, 'event', me));
+				me.consume();
+			}
+		});
+
+		this.graph.addListener(mxEvent.FIRE_MOUSE_EVENT, this.forcePanningHandler);
+		
+		// Handles pinch gestures
+		this.gestureHandler = mxUtils.bind(this, function(sender, eo)
+		{
+			if (this.isPinchEnabled())
+			{
+				var evt = eo.getProperty('event');
+				
+				if (!mxEvent.isConsumed(evt) && evt.type == 'gesturestart')
+				{
+					this.initialScale = this.graph.view.scale;
+				
+					// Forces start of panning when pinch gesture starts
+					if (!this.active && this.mouseDownEvent != null)
+					{
+						this.start(this.mouseDownEvent);
+						this.mouseDownEvent = null;
+					}
+				}
+				else if (evt.type == 'gestureend' && this.initialScale != null)
+				{
+					this.initialScale = null;
+				}
+				
+				if (this.initialScale != null)
+				{
+					var value = Math.round(this.initialScale * evt.scale * 100) / 100;
+					
+					if (this.minScale != null)
+					{
+						value = Math.max(this.minScale, value);
+					}
+					
+					if (this.maxScale != null)
+					{
+						value = Math.min(this.maxScale, value);
+					}
+	
+					if (this.graph.view.scale != value)
+					{
+						this.graph.zoomTo(value);
+						mxEvent.consume(evt);
+					}
+				}
+			}
+		});
+		
+		this.graph.addListener(mxEvent.GESTURE, this.gestureHandler);
+	}
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxPanningHandler.prototype = new mxEventSource();
+mxPanningHandler.prototype.constructor = mxPanningHandler;
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxPanningHandler.prototype.graph = null;
+
+/**
+ * Variable: useLeftButtonForPanning
+ * 
+ * Specifies if panning should be active for the left mouse button.
+ * Setting this to true may conflict with <mxRubberband>. Default is false.
+ */
+mxPanningHandler.prototype.useLeftButtonForPanning = false;
+
+/**
+ * Variable: usePopupTrigger
+ * 
+ * Specifies if <mxEvent.isPopupTrigger> should also be used for panning.
+ */
+mxPanningHandler.prototype.usePopupTrigger = true;
+
+/**
+ * Variable: ignoreCell
+ * 
+ * Specifies if panning should be active even if there is a cell under the
+ * mousepointer. Default is false.
+ */
+mxPanningHandler.prototype.ignoreCell = false;
+
+/**
+ * Variable: previewEnabled
+ * 
+ * Specifies if the panning should be previewed. Default is true.
+ */
+mxPanningHandler.prototype.previewEnabled = true;
+
+/**
+ * Variable: useGrid
+ * 
+ * Specifies if the panning steps should be aligned to the grid size.
+ * Default is false.
+ */
+mxPanningHandler.prototype.useGrid = false;
+
+/**
+ * Variable: panningEnabled
+ * 
+ * Specifies if panning should be enabled. Default is true.
+ */
+mxPanningHandler.prototype.panningEnabled = true;
+
+/**
+ * Variable: pinchEnabled
+ * 
+ * Specifies if pinch gestures should be handled as zoom. Default is true.
+ */
+mxPanningHandler.prototype.pinchEnabled = true;
+
+/**
+ * Variable: maxScale
+ * 
+ * Specifies the maximum scale. Default is 8.
+ */
+mxPanningHandler.prototype.maxScale = 8;
+
+/**
+ * Variable: minScale
+ * 
+ * Specifies the minimum scale. Default is 0.01.
+ */
+mxPanningHandler.prototype.minScale = 0.01;
+
+/**
+ * Variable: dx
+ * 
+ * Holds the current horizontal offset.
+ */
+mxPanningHandler.prototype.dx = null;
+
+/**
+ * Variable: dy
+ * 
+ * Holds the current vertical offset.
+ */
+mxPanningHandler.prototype.dy = null;
+
+/**
+ * Variable: startX
+ * 
+ * Holds the x-coordinate of the start point.
+ */
+mxPanningHandler.prototype.startX = 0;
+
+/**
+ * Variable: startY
+ * 
+ * Holds the y-coordinate of the start point.
+ */
+mxPanningHandler.prototype.startY = 0;
+
+/**
+ * Function: isActive
+ * 
+ * Returns true if the handler is currently active.
+ */
+mxPanningHandler.prototype.isActive = function()
+{
+	return this.active || this.initialScale != null;
+};
+
+/**
+ * Function: isPanningEnabled
+ * 
+ * Returns <panningEnabled>.
+ */
+mxPanningHandler.prototype.isPanningEnabled = function()
+{
+	return this.panningEnabled;
+};
+
+/**
+ * Function: setPanningEnabled
+ * 
+ * Sets <panningEnabled>.
+ */
+mxPanningHandler.prototype.setPanningEnabled = function(value)
+{
+	this.panningEnabled = value;
+};
+
+/**
+ * Function: isPinchEnabled
+ * 
+ * Returns <pinchEnabled>.
+ */
+mxPanningHandler.prototype.isPinchEnabled = function()
+{
+	return this.pinchEnabled;
+};
+
+/**
+ * Function: setPinchEnabled
+ * 
+ * Sets <pinchEnabled>.
+ */
+mxPanningHandler.prototype.setPinchEnabled = function(value)
+{
+	this.pinchEnabled = value;
+};
+
+/**
+ * Function: isPanningTrigger
+ * 
+ * Returns true if the given event is a panning trigger for the optional
+ * given cell. This returns true if control-shift is pressed or if
+ * <usePopupTrigger> is true and the event is a popup trigger.
+ */
+mxPanningHandler.prototype.isPanningTrigger = function(me)
+{
+	var evt = me.getEvent();
+	
+	return (this.useLeftButtonForPanning && me.getState() == null &&
+			mxEvent.isLeftMouseButton(evt)) || (mxEvent.isControlDown(evt) &&
+			mxEvent.isShiftDown(evt)) || (this.usePopupTrigger && mxEvent.isPopupTrigger(evt));
+};
+
+/**
+ * Function: isForcePanningEvent
+ * 
+ * Returns true if the given <mxMouseEvent> should start panning. This
+ * implementation always returns true if <ignoreCell> is true or for
+ * multi touch events.
+ */
+mxPanningHandler.prototype.isForcePanningEvent = function(me)
+{
+	return this.ignoreCell || mxEvent.isMultiTouchEvent(me.getEvent());
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by initiating the panning. By consuming the event all
+ * subsequent events of the gesture are redirected to this handler.
+ */
+mxPanningHandler.prototype.mouseDown = function(sender, me)
+{
+	this.mouseDownEvent = me;
+	
+	if (!me.isConsumed() && this.isPanningEnabled() && !this.active && this.isPanningTrigger(me))
+	{
+		this.start(me);
+		this.consumePanningTrigger(me);
+	}
+};
+
+/**
+ * Function: start
+ * 
+ * Starts panning at the given event.
+ */
+mxPanningHandler.prototype.start = function(me)
+{
+	this.dx0 = -this.graph.container.scrollLeft;
+	this.dy0 = -this.graph.container.scrollTop;
+
+	// Stores the location of the trigger event
+	this.startX = me.getX();
+	this.startY = me.getY();
+	this.dx = null;
+	this.dy = null;
+	
+	this.panningTrigger = true;
+};
+
+/**
+ * Function: consumePanningTrigger
+ * 
+ * Consumes the given <mxMouseEvent> if it was a panning trigger in
+ * <mouseDown>. The default is to invoke <mxMouseEvent.consume>. Note that this
+ * will block any further event processing. If you haven't disabled built-in
+ * context menus and require immediate selection of the cell on mouseDown in
+ * Safari and/or on the Mac, then use the following code:
+ * 
+ * (code)
+ * mxPanningHandler.prototype.consumePanningTrigger = function(me)
+ * {
+ *   if (me.evt.preventDefault)
+ *   {
+ *     me.evt.preventDefault();
+ *   }
+ *   
+ *   // Stops event processing in IE
+ *   me.evt.returnValue = false;
+ *   
+ *   // Sets local consumed state
+ *   if (!mxClient.IS_SF && !mxClient.IS_MAC)
+ *   {
+ *     me.consumed = true;
+ *   }
+ * };
+ * (end)
+ */
+mxPanningHandler.prototype.consumePanningTrigger = function(me)
+{
+	me.consume();
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating the panning on the graph.
+ */
+mxPanningHandler.prototype.mouseMove = function(sender, me)
+{
+	this.dx = me.getX() - this.startX;
+	this.dy = me.getY() - this.startY;
+	
+	if (this.active)
+	{
+		if (this.previewEnabled)
+		{
+			// Applies the grid to the panning steps
+			if (this.useGrid)
+			{
+				this.dx = this.graph.snap(this.dx);
+				this.dy = this.graph.snap(this.dy);
+			}
+			
+			this.graph.panGraph(this.dx + this.dx0, this.dy + this.dy0);
+		}
+
+		this.fireEvent(new mxEventObject(mxEvent.PAN, 'event', me));
+	}
+	else if (this.panningTrigger)
+	{
+		var tmp = this.active;
+
+		// Panning is activated only if the mouse is moved
+		// beyond the graph tolerance
+		this.active = Math.abs(this.dx) > this.graph.tolerance || Math.abs(this.dy) > this.graph.tolerance;
+
+		if (!tmp && this.active)
+		{
+			this.fireEvent(new mxEventObject(mxEvent.PAN_START, 'event', me));
+		}
+	}
+	
+	if (this.active || this.panningTrigger)
+	{
+		me.consume();
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by setting the translation on the view or showing the
+ * popupmenu.
+ */
+mxPanningHandler.prototype.mouseUp = function(sender, me)
+{
+	if (this.active)
+	{
+		if (this.dx != null && this.dy != null)
+		{
+			// Ignores if scrollbars have been used for panning
+			if (!this.graph.useScrollbarsForPanning || !mxUtils.hasScrollbars(this.graph.container))
+			{
+				var scale = this.graph.getView().scale;
+				var t = this.graph.getView().translate;
+				this.graph.panGraph(0, 0);
+				this.panGraph(t.x + this.dx / scale, t.y + this.dy / scale);
+			}
+			
+			me.consume();
+		}
+		
+		this.fireEvent(new mxEventObject(mxEvent.PAN_END, 'event', me));
+	}
+	
+	this.panningTrigger = false;
+	this.mouseDownEvent = null;
+	this.active = false;
+	this.dx = null;
+	this.dy = null;
+};
+
+/**
+ * Function: panGraph
+ * 
+ * Pans <graph> by the given amount.
+ */
+mxPanningHandler.prototype.panGraph = function(dx, dy)
+{
+	this.graph.getView().setTranslate(dx, dy);
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxPanningHandler.prototype.destroy = function()
+{
+	this.graph.removeMouseListener(this);
+	this.graph.removeListener(this.forcePanningHandler);
+	this.graph.removeListener(this.gestureHandler);
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/handler/mxPopupMenuHandler.js b/airavata-kubernetes/web-console/src/assets/js/handler/mxPopupMenuHandler.js
new file mode 100644
index 0000000..2388319
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/handler/mxPopupMenuHandler.js
@@ -0,0 +1,218 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxPopupMenuHandler
+ * 
+ * Event handler that creates popupmenus.
+ * 
+ * Constructor: mxPopupMenuHandler
+ * 
+ * Constructs an event handler that creates a <mxPopupMenu>.
+ */
+function mxPopupMenuHandler(graph, factoryMethod)
+{
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.factoryMethod = factoryMethod;
+		this.graph.addMouseListener(this);
+		
+		// Does not show menu if any touch gestures take place after the trigger
+		this.gestureHandler = mxUtils.bind(this, function(sender, eo)
+		{
+			this.inTolerance = false;
+		});
+		
+		this.graph.addListener(mxEvent.GESTURE, this.gestureHandler);
+		
+		this.init();
+	}
+};
+
+/**
+ * Extends mxPopupMenu.
+ */
+mxPopupMenuHandler.prototype = new mxPopupMenu();
+mxPopupMenuHandler.prototype.constructor = mxPopupMenuHandler;
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxPopupMenuHandler.prototype.graph = null;
+
+/**
+ * Variable: selectOnPopup
+ * 
+ * Specifies if cells should be selected if a popupmenu is displayed for
+ * them. Default is true.
+ */
+mxPopupMenuHandler.prototype.selectOnPopup = true;
+
+/**
+ * Variable: clearSelectionOnBackground
+ * 
+ * Specifies if cells should be deselected if a popupmenu is displayed for
+ * the diagram background. Default is true.
+ */
+mxPopupMenuHandler.prototype.clearSelectionOnBackground = true;
+
+/**
+ * Variable: triggerX
+ * 
+ * X-coordinate of the mouse down event.
+ */
+mxPopupMenuHandler.prototype.triggerX = null;
+
+/**
+ * Variable: triggerY
+ * 
+ * Y-coordinate of the mouse down event.
+ */
+mxPopupMenuHandler.prototype.triggerY = null;
+
+/**
+ * Variable: screenX
+ * 
+ * Screen X-coordinate of the mouse down event.
+ */
+mxPopupMenuHandler.prototype.screenX = null;
+
+/**
+ * Variable: screenY
+ * 
+ * Screen Y-coordinate of the mouse down event.
+ */
+mxPopupMenuHandler.prototype.screenY = null;
+
+/**
+ * Function: init
+ * 
+ * Initializes the shapes required for this vertex handler.
+ */
+mxPopupMenuHandler.prototype.init = function()
+{
+	// Supercall
+	mxPopupMenu.prototype.init.apply(this);
+
+	// Hides the tooltip if the mouse is over
+	// the context menu
+	mxEvent.addGestureListeners(this.div, mxUtils.bind(this, function(evt)
+	{
+		this.graph.tooltipHandler.hide();
+	}));
+};
+
+/**
+ * Function: isSelectOnPopup
+ * 
+ * Hook for returning if a cell should be selected for a given <mxMouseEvent>.
+ * This implementation returns <selectOnPopup>.
+ */
+mxPopupMenuHandler.prototype.isSelectOnPopup = function(me)
+{
+	return this.selectOnPopup;
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by initiating the panning. By consuming the event all
+ * subsequent events of the gesture are redirected to this handler.
+ */
+mxPopupMenuHandler.prototype.mouseDown = function(sender, me)
+{
+	if (this.isEnabled() && !mxEvent.isMultiTouchEvent(me.getEvent()))
+	{
+		// Hides the popupmenu if is is being displayed
+		this.hideMenu();
+		this.triggerX = me.getGraphX();
+		this.triggerY = me.getGraphY();
+		this.screenX = mxEvent.getMainEvent(me.getEvent()).screenX;
+		this.screenY = mxEvent.getMainEvent(me.getEvent()).screenY;
+		this.popupTrigger = this.isPopupTrigger(me);
+		this.inTolerance = true;
+	}
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating the panning on the graph.
+ */
+mxPopupMenuHandler.prototype.mouseMove = function(sender, me)
+{
+	// Popup trigger may change on mouseUp so ignore it
+	if (this.inTolerance && this.screenX != null && this.screenY != null)
+	{
+		if (Math.abs(mxEvent.getMainEvent(me.getEvent()).screenX - this.screenX) > this.graph.tolerance ||
+			Math.abs(mxEvent.getMainEvent(me.getEvent()).screenY - this.screenY) > this.graph.tolerance)
+		{
+			this.inTolerance = false;
+		}
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by setting the translation on the view or showing the
+ * popupmenu.
+ */
+mxPopupMenuHandler.prototype.mouseUp = function(sender, me)
+{
+	if (this.popupTrigger && this.inTolerance && this.triggerX != null && this.triggerY != null)
+	{
+		var cell = this.getCellForPopupEvent(me);
+
+		// Selects the cell for which the context menu is being displayed
+		if (this.graph.isEnabled() && this.isSelectOnPopup(me) &&
+			cell != null && !this.graph.isCellSelected(cell))
+		{
+			this.graph.setSelectionCell(cell);
+		}
+		else if (this.clearSelectionOnBackground && cell == null)
+		{
+			this.graph.clearSelection();
+		}
+		
+		// Hides the tooltip if there is one
+		this.graph.tooltipHandler.hide();
+
+		// Menu is shifted by 1 pixel so that the mouse up event
+		// is routed via the underlying shape instead of the DIV
+		var origin = mxUtils.getScrollOrigin();
+		this.popup(me.getX() + origin.x + 1, me.getY() + origin.y + 1, cell, me.getEvent());
+		me.consume();
+	}
+	
+	this.popupTrigger = false;
+	this.inTolerance = false;
+};
+
+/**
+ * Function: getCellForPopupEvent
+ * 
+ * Hook to return the cell for the mouse up popup trigger handling.
+ */
+mxPopupMenuHandler.prototype.getCellForPopupEvent = function(me)
+{
+	return me.getCell();
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxPopupMenuHandler.prototype.destroy = function()
+{
+	this.graph.removeMouseListener(this);
+	this.graph.removeListener(this.gestureHandler);
+	
+	// Supercall
+	mxPopupMenu.prototype.destroy.apply(this);
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/handler/mxRubberband.js b/airavata-kubernetes/web-console/src/assets/js/handler/mxRubberband.js
new file mode 100644
index 0000000..8d29fdb
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/handler/mxRubberband.js
@@ -0,0 +1,401 @@
+/**
+ * Copyright (c) 2006-2016, JGraph Ltd
+ * Copyright (c) 2006-2016, Gaudenz Alder
+ */
+/**
+ * Class: mxRubberband
+ * 
+ * Event handler that selects rectangular regions. This is not built-into
+ * <mxGraph>. To enable rubberband selection in a graph, use the following code.
+ * 
+ * Example:
+ * 
+ * (code)
+ * var rubberband = new mxRubberband(graph);
+ * (end)
+ * 
+ * Constructor: mxRubberband
+ * 
+ * Constructs an event handler that selects rectangular regions in the graph
+ * using rubberband selection.
+ */
+function mxRubberband(graph)
+{
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.graph.addMouseListener(this);
+
+		// Handles force rubberband event
+		this.forceRubberbandHandler = mxUtils.bind(this, function(sender, evt)
+		{
+			var evtName = evt.getProperty('eventName');
+			var me = evt.getProperty('event');
+			
+			if (evtName == mxEvent.MOUSE_DOWN && this.isForceRubberbandEvent(me))
+			{
+				var offset = mxUtils.getOffset(this.graph.container);
+				var origin = mxUtils.getScrollOrigin(this.graph.container);
+				origin.x -= offset.x;
+				origin.y -= offset.y;
+				this.start(me.getX() + origin.x, me.getY() + origin.y);
+				me.consume(false);
+			}
+		});
+		
+		this.graph.addListener(mxEvent.FIRE_MOUSE_EVENT, this.forceRubberbandHandler);
+		
+		// Repaints the marquee after autoscroll
+		this.panHandler = mxUtils.bind(this, function()
+		{
+			this.repaint();
+		});
+		
+		this.graph.addListener(mxEvent.PAN, this.panHandler);
+		
+		// Does not show menu if any touch gestures take place after the trigger
+		this.gestureHandler = mxUtils.bind(this, function(sender, eo)
+		{
+			if (this.first != null)
+			{
+				this.reset();
+			}
+		});
+		
+		this.graph.addListener(mxEvent.GESTURE, this.gestureHandler);
+		
+		// Automatic deallocation of memory
+		if (mxClient.IS_IE)
+		{
+			mxEvent.addListener(window, 'unload',
+				mxUtils.bind(this, function()
+				{
+					this.destroy();
+				})
+			);
+		}
+	}
+};
+
+/**
+ * Variable: defaultOpacity
+ * 
+ * Specifies the default opacity to be used for the rubberband div. Default
+ * is 20.
+ */
+mxRubberband.prototype.defaultOpacity = 20;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxRubberband.prototype.enabled = true;
+
+/**
+ * Variable: div
+ * 
+ * Holds the DIV element which is currently visible.
+ */
+mxRubberband.prototype.div = null;
+
+/**
+ * Variable: sharedDiv
+ * 
+ * Holds the DIV element which is used to display the rubberband.
+ */
+mxRubberband.prototype.sharedDiv = null;
+
+/**
+ * Variable: currentX
+ * 
+ * Holds the value of the x argument in the last call to <update>.
+ */
+mxRubberband.prototype.currentX = 0;
+
+/**
+ * Variable: currentY
+ * 
+ * Holds the value of the y argument in the last call to <update>.
+ */
+mxRubberband.prototype.currentY = 0;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation returns
+ * <enabled>.
+ */
+mxRubberband.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+		
+/**
+ * Function: setEnabled
+ * 
+ * Enables or disables event handling. This implementation updates
+ * <enabled>.
+ */
+mxRubberband.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: isForceRubberbandEvent
+ * 
+ * Returns true if the given <mxMouseEvent> should start rubberband selection.
+ * This implementation returns true if the alt key is pressed.
+ */
+mxRubberband.prototype.isForceRubberbandEvent = function(me)
+{
+	return mxEvent.isAltDown(me.getEvent());
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by initiating a rubberband selection. By consuming the
+ * event all subsequent events of the gesture are redirected to this
+ * handler.
+ */
+mxRubberband.prototype.mouseDown = function(sender, me)
+{
+	if (!me.isConsumed() && this.isEnabled() && this.graph.isEnabled() &&
+		me.getState() == null && !mxEvent.isMultiTouchEvent(me.getEvent()))
+	{
+		var offset = mxUtils.getOffset(this.graph.container);
+		var origin = mxUtils.getScrollOrigin(this.graph.container);
+		origin.x -= offset.x;
+		origin.y -= offset.y;
+		this.start(me.getX() + origin.x, me.getY() + origin.y);
+
+		// Does not prevent the default for this event so that the
+		// event processing chain is still executed even if we start
+		// rubberbanding. This is required eg. in ExtJs to hide the
+		// current context menu. In mouseMove we'll make sure we're
+		// not selecting anything while we're rubberbanding.
+		me.consume(false);
+	}
+};
+
+/**
+ * Function: start
+ * 
+ * Sets the start point for the rubberband selection.
+ */
+mxRubberband.prototype.start = function(x, y)
+{
+	this.first = new mxPoint(x, y);
+
+	var container = this.graph.container;
+	
+	function createMouseEvent(evt)
+	{
+		var me = new mxMouseEvent(evt);
+		var pt = mxUtils.convertPoint(container, me.getX(), me.getY());
+		
+		me.graphX = pt.x;
+		me.graphY = pt.y;
+		
+		return me;
+	};
+
+	this.dragHandler = mxUtils.bind(this, function(evt)
+	{
+		this.mouseMove(this.graph, createMouseEvent(evt));
+	});
+
+	this.dropHandler = mxUtils.bind(this, function(evt)
+	{
+		this.mouseUp(this.graph, createMouseEvent(evt));
+	});
+
+	// Workaround for rubberband stopping if the mouse leaves the container in Firefox
+	if (mxClient.IS_FF)
+	{
+		mxEvent.addGestureListeners(document, null, this.dragHandler, this.dropHandler);
+	}
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating therubberband selection.
+ */
+mxRubberband.prototype.mouseMove = function(sender, me)
+{
+	if (!me.isConsumed() && this.first != null)
+	{
+		var origin = mxUtils.getScrollOrigin(this.graph.container);
+		var offset = mxUtils.getOffset(this.graph.container);
+		origin.x -= offset.x;
+		origin.y -= offset.y;
+		var x = me.getX() + origin.x;
+		var y = me.getY() + origin.y;
+		var dx = this.first.x - x;
+		var dy = this.first.y - y;
+		var tol = this.graph.tolerance;
+		
+		if (this.div != null || Math.abs(dx) > tol ||  Math.abs(dy) > tol)
+		{
+			if (this.div == null)
+			{
+				this.div = this.createShape();
+			}
+			
+			// Clears selection while rubberbanding. This is required because
+			// the event is not consumed in mouseDown.
+			mxUtils.clearSelection();
+			
+			this.update(x, y);
+			me.consume();
+		}
+	}
+};
+
+/**
+ * Function: createShape
+ * 
+ * Creates the rubberband selection shape.
+ */
+mxRubberband.prototype.createShape = function()
+{
+	if (this.sharedDiv == null)
+	{
+		this.sharedDiv = document.createElement('div');
+		this.sharedDiv.className = 'mxRubberband';
+		mxUtils.setOpacity(this.sharedDiv, this.defaultOpacity);
+	}
+
+	this.graph.container.appendChild(this.sharedDiv);
+		
+	return this.sharedDiv;
+};
+
+/**
+ * Function: isActive
+ * 
+ * Returns true if this handler is active.
+ */
+mxRubberband.prototype.isActive = function(sender, me)
+{
+	return this.div != null && this.div.style.display != 'none';
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by selecting the region of the rubberband using
+ * <mxGraph.selectRegion>.
+ */
+mxRubberband.prototype.mouseUp = function(sender, me)
+{
+	var active = this.isActive();
+	this.reset();
+	
+	if (active)
+	{
+		this.execute(me.getEvent());
+		me.consume();
+	}
+};
+
+/**
+ * Function: execute
+ * 
+ * Resets the state of this handler and selects the current region
+ * for the given event.
+ */
+mxRubberband.prototype.execute = function(evt)
+{
+	var rect = new mxRectangle(this.x, this.y, this.width, this.height);
+	this.graph.selectRegion(rect, evt);
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of the rubberband selection.
+ */
+mxRubberband.prototype.reset = function()
+{
+	if (this.div != null)
+	{
+		this.div.parentNode.removeChild(this.div);
+	}
+
+	mxEvent.removeGestureListeners(document, null, this.dragHandler, this.dropHandler);
+	this.dragHandler = null;
+	this.dropHandler = null;
+	
+	this.currentX = 0;
+	this.currentY = 0;
+	this.first = null;
+	this.div = null;
+};
+
+/**
+ * Function: update
+ * 
+ * Sets <currentX> and <currentY> and calls <repaint>.
+ */
+mxRubberband.prototype.update = function(x, y)
+{
+	this.currentX = x;
+	this.currentY = y;
+	
+	this.repaint();
+};
+
+/**
+ * Function: repaint
+ * 
+ * Computes the bounding box and updates the style of the <div>.
+ */
+mxRubberband.prototype.repaint = function()
+{
+	if (this.div != null)
+	{
+		var x = this.currentX - this.graph.panDx;
+		var y = this.currentY - this.graph.panDy;
+		
+		this.x = Math.min(this.first.x, x);
+		this.y = Math.min(this.first.y, y);
+		this.width = Math.max(this.first.x, x) - this.x;
+		this.height =  Math.max(this.first.y, y) - this.y;
+
+		var dx = (mxClient.IS_VML) ? this.graph.panDx : 0;
+		var dy = (mxClient.IS_VML) ? this.graph.panDy : 0;
+		
+		this.div.style.left = (this.x + dx) + 'px';
+		this.div.style.top = (this.y + dy) + 'px';
+		this.div.style.width = Math.max(1, this.width) + 'px';
+		this.div.style.height = Math.max(1, this.height) + 'px';
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes. This does
+ * normally not need to be called, it is called automatically when the
+ * window unloads.
+ */
+mxRubberband.prototype.destroy = function()
+{
+	if (!this.destroyed)
+	{
+		this.destroyed = true;
+		this.graph.removeMouseListener(this);
+		this.graph.removeListener(this.forceRubberbandHandler);
+		this.graph.removeListener(this.panHandler);
+		this.reset();
+		
+		if (this.sharedDiv != null)
+		{
+			this.sharedDiv = null;
+		}
+	}
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/handler/mxSelectionCellsHandler.js b/airavata-kubernetes/web-console/src/assets/js/handler/mxSelectionCellsHandler.js
new file mode 100644
index 0000000..432e237
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/handler/mxSelectionCellsHandler.js
@@ -0,0 +1,287 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxSelectionCellsHandler
+ * 
+ * An event handler that manages cell handlers and invokes their mouse event
+ * processing functions.
+ * 
+ * Group: Events
+ * 
+ * Event: mxEvent.ADD
+ * 
+ * Fires if a cell has been added to the selection. The <code>state</code>
+ * property contains the <mxCellState> that has been added.
+ * 
+ * Event: mxEvent.REMOVE
+ * 
+ * Fires if a cell has been remove from the selection. The <code>state</code>
+ * property contains the <mxCellState> that has been removed.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ */
+function mxSelectionCellsHandler(graph)
+{
+	mxEventSource.call(this);
+	
+	this.graph = graph;
+	this.handlers = new mxDictionary();
+	this.graph.addMouseListener(this);
+	
+	this.refreshHandler = mxUtils.bind(this, function(sender, evt)
+	{
+		if (this.isEnabled())
+		{
+			this.refresh();
+		}
+	});
+	
+	this.graph.getSelectionModel().addListener(mxEvent.CHANGE, this.refreshHandler);
+	this.graph.getModel().addListener(mxEvent.CHANGE, this.refreshHandler);
+	this.graph.getView().addListener(mxEvent.SCALE, this.refreshHandler);
+	this.graph.getView().addListener(mxEvent.TRANSLATE, this.refreshHandler);
+	this.graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, this.refreshHandler);
+	this.graph.getView().addListener(mxEvent.DOWN, this.refreshHandler);
+	this.graph.getView().addListener(mxEvent.UP, this.refreshHandler);
+};
+
+/**
+ * Extends mxEventSource.
+ */
+mxUtils.extend(mxSelectionCellsHandler, mxEventSource);
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxSelectionCellsHandler.prototype.graph = null;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxSelectionCellsHandler.prototype.enabled = true;
+
+/**
+ * Variable: refreshHandler
+ * 
+ * Keeps a reference to an event listener for later removal.
+ */
+mxSelectionCellsHandler.prototype.refreshHandler = null;
+
+/**
+ * Variable: maxHandlers
+ * 
+ * Defines the maximum number of handlers to paint individually. Default is 100.
+ */
+mxSelectionCellsHandler.prototype.maxHandlers = 100;
+
+/**
+ * Variable: handlers
+ * 
+ * <mxDictionary> that maps from cells to handlers.
+ */
+mxSelectionCellsHandler.prototype.handlers = null;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns <enabled>.
+ */
+mxSelectionCellsHandler.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ * 
+ * Sets <enabled>.
+ */
+mxSelectionCellsHandler.prototype.setEnabled = function(value)
+{
+	this.enabled = value;
+};
+
+/**
+ * Function: getHandler
+ * 
+ * Returns the handler for the given cell.
+ */
+mxSelectionCellsHandler.prototype.getHandler = function(cell)
+{
+	return this.handlers.get(cell);
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets all handlers.
+ */
+mxSelectionCellsHandler.prototype.reset = function()
+{
+	this.handlers.visit(function(key, handler)
+	{
+		handler.reset.apply(handler);
+	});
+};
+
+/**
+ * Function: refresh
+ * 
+ * Reloads or updates all handlers.
+ */
+mxSelectionCellsHandler.prototype.refresh = function()
+{
+	// Removes all existing handlers
+	var oldHandlers = this.handlers;
+	this.handlers = new mxDictionary();
+	
+	// Creates handles for all selection cells
+	var tmp = this.graph.getSelectionCells();
+
+	for (var i = 0; i < tmp.length; i++)
+	{
+		var state = this.graph.view.getState(tmp[i]);
+
+		if (state != null)
+		{
+			var handler = oldHandlers.remove(tmp[i]);
+
+			if (handler != null)
+			{
+				if (handler.state != state)
+				{
+					handler.destroy();
+					handler = null;
+				}
+				else
+				{
+					if (handler.refresh != null)
+					{
+						handler.refresh();
+					}
+					
+					handler.redraw();
+				}
+			}
+			
+			if (handler == null)
+			{
+				handler = this.graph.createHandler(state);
+				this.fireEvent(new mxEventObject(mxEvent.ADD, 'state', state));
+			}
+			
+			if (handler != null)
+			{
+				this.handlers.put(tmp[i], handler);
+			}
+		}
+	}
+	
+	// Destroys all unused handlers
+	oldHandlers.visit(mxUtils.bind(this, function(key, handler)
+	{
+		this.fireEvent(new mxEventObject(mxEvent.REMOVE, 'state', handler.state));
+		handler.destroy();
+	}));
+};
+
+/**
+ * Function: updateHandler
+ * 
+ * Updates the handler for the given shape if one exists.
+ */
+mxSelectionCellsHandler.prototype.updateHandler = function(state)
+{
+	var handler = this.handlers.remove(state.cell);
+	
+	if (handler != null)
+	{
+		handler.destroy();
+		handler = this.graph.createHandler(state);
+		
+		if (handler != null)
+		{
+			this.handlers.put(state.cell, handler);
+		}
+	}
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Redirects the given event to the handlers.
+ */
+mxSelectionCellsHandler.prototype.mouseDown = function(sender, me)
+{
+	if (this.graph.isEnabled() && this.isEnabled())
+	{
+		var args = [sender, me];
+
+		this.handlers.visit(function(key, handler)
+		{
+			handler.mouseDown.apply(handler, args);
+		});
+	}
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Redirects the given event to the handlers.
+ */
+mxSelectionCellsHandler.prototype.mouseMove = function(sender, me)
+{
+	if (this.graph.isEnabled() && this.isEnabled())
+	{
+		var args = [sender, me];
+
+		this.handlers.visit(function(key, handler)
+		{
+			handler.mouseMove.apply(handler, args);
+		});
+	}
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Redirects the given event to the handlers.
+ */
+mxSelectionCellsHandler.prototype.mouseUp = function(sender, me)
+{
+	if (this.graph.isEnabled() && this.isEnabled())
+	{
+		var args = [sender, me];
+
+		this.handlers.visit(function(key, handler)
+		{
+			handler.mouseUp.apply(handler, args);
+		});
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxSelectionCellsHandler.prototype.destroy = function()
+{
+	this.graph.removeMouseListener(this);
+	
+	if (this.refreshHandler != null)
+	{
+		this.graph.getSelectionModel().removeListener(this.refreshHandler);
+		this.graph.getModel().removeListener(this.refreshHandler);
+		this.graph.getView().removeListener(this.refreshHandler);
+		this.refreshHandler = null;
+	}
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/handler/mxTooltipHandler.js b/airavata-kubernetes/web-console/src/assets/js/handler/mxTooltipHandler.js
new file mode 100644
index 0000000..4237e0c
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/handler/mxTooltipHandler.js
@@ -0,0 +1,337 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxTooltipHandler
+ * 
+ * Graph event handler that displays tooltips. <mxGraph.getTooltip> is used to
+ * get the tooltip for a cell or handle. This handler is built-into
+ * <mxGraph.tooltipHandler> and enabled using <mxGraph.setTooltips>.
+ *
+ * Example:
+ * 
+ * (code>
+ * new mxTooltipHandler(graph);
+ * (end)
+ * 
+ * Constructor: mxTooltipHandler
+ * 
+ * Constructs an event handler that displays tooltips with the specified
+ * delay (in milliseconds). If no delay is specified then a default delay
+ * of 500 ms (0.5 sec) is used.
+ * 
+ * Parameters:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * delay - Optional delay in milliseconds.
+ */
+function mxTooltipHandler(graph, delay)
+{
+	if (graph != null)
+	{
+		this.graph = graph;
+		this.delay = delay || 500;
+		this.graph.addMouseListener(this);
+	}
+};
+
+/**
+ * Variable: zIndex
+ * 
+ * Specifies the zIndex for the tooltip and its shadow. Default is 10005.
+ */
+mxTooltipHandler.prototype.zIndex = 10005;
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxTooltipHandler.prototype.graph = null;
+
+/**
+ * Variable: delay
+ * 
+ * Delay to show the tooltip in milliseconds. Default is 500.
+ */
+mxTooltipHandler.prototype.delay = null;
+
+/**
+ * Variable: ignoreTouchEvents
+ * 
+ * Specifies if touch and pen events should be ignored. Default is true.
+ */
+mxTooltipHandler.prototype.ignoreTouchEvents = true;
+
+/**
+ * Variable: hideOnHover
+ * 
+ * Specifies if the tooltip should be hidden if the mouse is moved over the
+ * current cell. Default is false.
+ */
+mxTooltipHandler.prototype.hideOnHover = false;
+
+/**
+ * Variable: destroyed
+ * 
+ * True if this handler was destroyed using <destroy>.
+ */
+mxTooltipHandler.prototype.destroyed = false;
+
+/**
+ * Variable: enabled
+ * 
+ * Specifies if events are handled. Default is true.
+ */
+mxTooltipHandler.prototype.enabled = true;
+
+/**
+ * Function: isEnabled
+ * 
+ * Returns true if events are handled. This implementation
+ * returns <enabled>.
+ */
+mxTooltipHandler.prototype.isEnabled = function()
+{
+	return this.enabled;
+};
+
+/**
+ * Function: setEnabled
+ * 
+ * Enables or disables event handling. This implementation
+ * updates <enabled>.
+ */
+mxTooltipHandler.prototype.setEnabled = function(enabled)
+{
+	this.enabled = enabled;
+};
+
+/**
+ * Function: isHideOnHover
+ * 
+ * Returns <hideOnHover>.
+ */
+mxTooltipHandler.prototype.isHideOnHover = function()
+{
+	return this.hideOnHover;
+};
+
+/**
+ * Function: setHideOnHover
+ * 
+ * Sets <hideOnHover>.
+ */
+mxTooltipHandler.prototype.setHideOnHover = function(value)
+{
+	this.hideOnHover = value;
+};
+
+/**
+ * Function: init
+ * 
+ * Initializes the DOM nodes required for this tooltip handler.
+ */
+mxTooltipHandler.prototype.init = function()
+{
+	if (document.body != null)
+	{
+		this.div = document.createElement('div');
+		this.div.className = 'mxTooltip';
+		this.div.style.visibility = 'hidden';
+
+		document.body.appendChild(this.div);
+
+		mxEvent.addGestureListeners(this.div, mxUtils.bind(this, function(evt)
+		{
+			this.hideTooltip();
+		}));
+	}
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event by initiating a rubberband selection. By consuming the
+ * event all subsequent events of the gesture are redirected to this
+ * handler.
+ */
+mxTooltipHandler.prototype.mouseDown = function(sender, me)
+{
+	this.reset(me, false);
+	this.hideTooltip();
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating the rubberband selection.
+ */
+mxTooltipHandler.prototype.mouseMove = function(sender, me)
+{
+	if (me.getX() != this.lastX || me.getY() != this.lastY)
+	{
+		this.reset(me, true);
+		
+		if (this.isHideOnHover() || me.getState() != this.state || (me.getSource() != this.node &&
+			(!this.stateSource || (me.getState() != null && this.stateSource ==
+			(me.isSource(me.getState().shape) || !me.isSource(me.getState().text))))))
+		{
+			this.hideTooltip();
+		}
+	}
+	
+	this.lastX = me.getX();
+	this.lastY = me.getY();
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by resetting the tooltip timer or hiding the existing
+ * tooltip.
+ */
+mxTooltipHandler.prototype.mouseUp = function(sender, me)
+{
+	this.reset(me, true);
+	this.hideTooltip();
+};
+
+
+/**
+ * Function: resetTimer
+ * 
+ * Resets the timer.
+ */
+mxTooltipHandler.prototype.resetTimer = function()
+{
+	if (this.thread != null)
+	{
+		window.clearTimeout(this.thread);
+		this.thread = null;
+	}
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets and/or restarts the timer to trigger the display of the tooltip.
+ */
+mxTooltipHandler.prototype.reset = function(me, restart)
+{
+	if (!this.ignoreTouchEvents || mxEvent.isMouseEvent(me.getEvent()))
+	{
+		this.resetTimer();
+		
+		if (restart && this.isEnabled() && me.getState() != null && (this.div == null ||
+			this.div.style.visibility == 'hidden'))
+		{
+			var state = me.getState();
+			var node = me.getSource();
+			var x = me.getX();
+			var y = me.getY();
+			var stateSource = me.isSource(state.shape) || me.isSource(state.text);
+	
+			this.thread = window.setTimeout(mxUtils.bind(this, function()
+			{
+				if (!this.graph.isEditing() && !this.graph.popupMenuHandler.isMenuShowing() && !this.graph.isMouseDown)
+				{
+					// Uses information from inside event cause using the event at
+					// this (delayed) point in time is not possible in IE as it no
+					// longer contains the required information (member not found)
+					var tip = this.graph.getTooltip(state, node, x, y);
+					this.show(tip, x, y);
+					this.state = state;
+					this.node = node;
+					this.stateSource = stateSource;
+				}
+			}), this.delay);
+		}
+	}
+};
+
+/**
+ * Function: hide
+ * 
+ * Hides the tooltip and resets the timer.
+ */
+mxTooltipHandler.prototype.hide = function()
+{
+	this.resetTimer();
+	this.hideTooltip();
+};
+
+/**
+ * Function: hideTooltip
+ * 
+ * Hides the tooltip.
+ */
+mxTooltipHandler.prototype.hideTooltip = function()
+{
+	if (this.div != null)
+	{
+		this.div.style.visibility = 'hidden';
+		this.div.innerHTML = '';
+	}
+};
+
+/**
+ * Function: show
+ * 
+ * Shows the tooltip for the specified cell and optional index at the
+ * specified location (with a vertical offset of 10 pixels).
+ */
+mxTooltipHandler.prototype.show = function(tip, x, y)
+{
+	if (!this.destroyed && tip != null && tip.length > 0)
+	{
+		// Initializes the DOM nodes if required
+		if (this.div == null)
+		{
+			this.init();
+		}
+		
+		var origin = mxUtils.getScrollOrigin();
+
+		this.div.style.zIndex = this.zIndex;
+		this.div.style.left = (x + origin.x) + 'px';
+		this.div.style.top = (y + mxConstants.TOOLTIP_VERTICAL_OFFSET +
+			origin.y) + 'px';
+
+		if (!mxUtils.isNode(tip))
+		{	
+			this.div.innerHTML = tip.replace(/\n/g, '<br>');
+		}
+		else
+		{
+			this.div.innerHTML = '';
+			this.div.appendChild(tip);
+		}
+		
+		this.div.style.visibility = '';
+		mxUtils.fit(this.div);
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxTooltipHandler.prototype.destroy = function()
+{
+	if (!this.destroyed)
+	{
+		this.graph.removeMouseListener(this);
+		mxEvent.release(this.div);
+		
+		if (this.div != null && this.div.parentNode != null)
+		{
+			this.div.parentNode.removeChild(this.div);
+		}
+		
+		this.destroyed = true;
+		this.div = null;
+	}
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/handler/mxVertexHandler.js b/airavata-kubernetes/web-console/src/assets/js/handler/mxVertexHandler.js
new file mode 100644
index 0000000..7669d8c
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/handler/mxVertexHandler.js
@@ -0,0 +1,1950 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxVertexHandler
+ * 
+ * Event handler for resizing cells. This handler is automatically created in
+ * <mxGraph.createHandler>.
+ * 
+ * Constructor: mxVertexHandler
+ * 
+ * Constructs an event handler that allows to resize vertices
+ * and groups.
+ * 
+ * Parameters:
+ * 
+ * state - <mxCellState> of the cell to be resized.
+ */
+function mxVertexHandler(state)
+{
+	if (state != null)
+	{
+		this.state = state;
+		this.init();
+		
+		// Handles escape keystrokes
+		this.escapeHandler = mxUtils.bind(this, function(sender, evt)
+		{
+			if (this.livePreview && this.index != null)
+			{
+				// Redraws the live preview
+				this.state.view.graph.cellRenderer.redraw(this.state, true);
+				
+				// Redraws connected edges
+				this.state.view.invalidate(this.state.cell);
+				this.state.invalid = false;
+				this.state.view.validate();
+			}
+			
+			this.reset();
+		});
+		
+		this.state.view.graph.addListener(mxEvent.ESCAPE, this.escapeHandler);
+	}
+};
+
+/**
+ * Variable: graph
+ * 
+ * Reference to the enclosing <mxGraph>.
+ */
+mxVertexHandler.prototype.graph = null;
+
+/**
+ * Variable: state
+ * 
+ * Reference to the <mxCellState> being modified.
+ */
+mxVertexHandler.prototype.state = null;
+
+/**
+ * Variable: singleSizer
+ * 
+ * Specifies if only one sizer handle at the bottom, right corner should be
+ * used. Default is false.
+ */
+mxVertexHandler.prototype.singleSizer = false;
+
+/**
+ * Variable: index
+ * 
+ * Holds the index of the current handle.
+ */
+mxVertexHandler.prototype.index = null;
+
+/**
+ * Variable: allowHandleBoundsCheck
+ * 
+ * Specifies if the bounds of handles should be used for hit-detection in IE or
+ * if <tolerance> > 0. Default is true.
+ */
+mxVertexHandler.prototype.allowHandleBoundsCheck = true;
+
+/**
+ * Variable: handleImage
+ * 
+ * Optional <mxImage> to be used as handles. Default is null.
+ */
+mxVertexHandler.prototype.handleImage = null;
+
+/**
+ * Variable: tolerance
+ * 
+ * Optional tolerance for hit-detection in <getHandleForEvent>. Default is 0.
+ */
+mxVertexHandler.prototype.tolerance = 0;
+
+/**
+ * Variable: rotationEnabled
+ * 
+ * Specifies if a rotation handle should be visible. Default is false.
+ */
+mxVertexHandler.prototype.rotationEnabled = false;
+
+/**
+ * Variable: parentHighlightEnabled
+ * 
+ * Specifies if the parent should be highlighted if a child cell is selected.
+ * Default is false.
+ */
+mxVertexHandler.prototype.parentHighlightEnabled = false;
+
+/**
+ * Variable: rotationRaster
+ * 
+ * Specifies if rotation steps should be "rasterized" depening on the distance
+ * to the handle. Default is true.
+ */
+mxVertexHandler.prototype.rotationRaster = true;
+
+/**
+ * Variable: rotationCursor
+ * 
+ * Specifies the cursor for the rotation handle. Default is 'crosshair'.
+ */
+mxVertexHandler.prototype.rotationCursor = 'crosshair';
+
+/**
+ * Variable: livePreview
+ * 
+ * Specifies if resize should change the cell in-place. This is an experimental
+ * feature for non-touch devices. Default is false.
+ */
+mxVertexHandler.prototype.livePreview = false;
+
+/**
+ * Variable: manageSizers
+ * 
+ * Specifies if sizers should be hidden and spaced if the vertex is small.
+ * Default is false.
+ */
+mxVertexHandler.prototype.manageSizers = false;
+
+/**
+ * Variable: constrainGroupByChildren
+ * 
+ * Specifies if the size of groups should be constrained by the children.
+ * Default is false.
+ */
+mxVertexHandler.prototype.constrainGroupByChildren = false;
+
+/**
+ * Variable: rotationHandleVSpacing
+ * 
+ * Vertical spacing for rotation icon. Default is -16.
+ */
+mxVertexHandler.prototype.rotationHandleVSpacing = -16;
+
+/**
+ * Variable: horizontalOffset
+ * 
+ * The horizontal offset for the handles. This is updated in <redrawHandles>
+ * if <manageSizers> is true and the sizers are offset horizontally.
+ */
+mxVertexHandler.prototype.horizontalOffset = 0;
+
+/**
+ * Variable: verticalOffset
+ * 
+ * The horizontal offset for the handles. This is updated in <redrawHandles>
+ * if <manageSizers> is true and the sizers are offset vertically.
+ */
+mxVertexHandler.prototype.verticalOffset = 0;
+
+/**
+ * Function: init
+ * 
+ * Initializes the shapes required for this vertex handler.
+ */
+mxVertexHandler.prototype.init = function()
+{
+	this.graph = this.state.view.graph;
+	this.selectionBounds = this.getSelectionBounds(this.state);
+	this.bounds = new mxRectangle(this.selectionBounds.x, this.selectionBounds.y, this.selectionBounds.width, this.selectionBounds.height);
+	this.selectionBorder = this.createSelectionShape(this.bounds);
+	// VML dialect required here for event transparency in IE
+	this.selectionBorder.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+	this.selectionBorder.pointerEvents = false;
+	this.selectionBorder.rotation = Number(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+	this.selectionBorder.init(this.graph.getView().getOverlayPane());
+	mxEvent.redirectMouseEvents(this.selectionBorder.node, this.graph, this.state);
+	
+	if (this.graph.isCellMovable(this.state.cell))
+	{
+		this.selectionBorder.setCursor(mxConstants.CURSOR_MOVABLE_VERTEX);
+	}
+
+	// Adds the sizer handles
+	if (mxGraphHandler.prototype.maxCells <= 0 || this.graph.getSelectionCount() < mxGraphHandler.prototype.maxCells)
+	{
+		var resizable = this.graph.isCellResizable(this.state.cell);
+		this.sizers = [];
+
+		if (resizable || (this.graph.isLabelMovable(this.state.cell) &&
+			this.state.width >= 2 && this.state.height >= 2))
+		{
+			var i = 0;
+
+			if (resizable)
+			{
+				if (!this.singleSizer)
+				{
+					this.sizers.push(this.createSizer('nw-resize', i++));
+					this.sizers.push(this.createSizer('n-resize', i++));
+					this.sizers.push(this.createSizer('ne-resize', i++));
+					this.sizers.push(this.createSizer('w-resize', i++));
+					this.sizers.push(this.createSizer('e-resize', i++));
+					this.sizers.push(this.createSizer('sw-resize', i++));
+					this.sizers.push(this.createSizer('s-resize', i++));
+				}
+				
+				this.sizers.push(this.createSizer('se-resize', i++));
+			}
+			
+			var geo = this.graph.model.getGeometry(this.state.cell);
+			
+			if (geo != null && !geo.relative && !this.graph.isSwimlane(this.state.cell) &&
+				this.graph.isLabelMovable(this.state.cell))
+			{
+				// Marks this as the label handle for getHandleForEvent
+				this.labelShape = this.createSizer(mxConstants.CURSOR_LABEL_HANDLE, mxEvent.LABEL_HANDLE, mxConstants.LABEL_HANDLE_SIZE, mxConstants.LABEL_HANDLE_FILLCOLOR);
+				this.sizers.push(this.labelShape);
+			}
+		}
+		else if (this.graph.isCellMovable(this.state.cell) && !this.graph.isCellResizable(this.state.cell) &&
+			this.state.width < 2 && this.state.height < 2)
+		{
+			this.labelShape = this.createSizer(mxConstants.CURSOR_MOVABLE_VERTEX,
+				mxEvent.LABEL_HANDLE, null, mxConstants.LABEL_HANDLE_FILLCOLOR);
+			this.sizers.push(this.labelShape);
+		}
+	}
+	
+	// Adds the rotation handler
+	if (this.isRotationHandleVisible())
+	{
+		this.rotationShape = this.createSizer(this.rotationCursor, mxEvent.ROTATION_HANDLE,
+			mxConstants.HANDLE_SIZE + 3, mxConstants.HANDLE_FILLCOLOR);
+		this.sizers.push(this.rotationShape);
+	}
+
+	this.customHandles = this.createCustomHandles();
+	this.redraw();
+	
+	if (this.constrainGroupByChildren)
+	{
+		this.updateMinBounds();
+	}
+};
+
+/**
+ * Function: isRotationHandleVisible
+ * 
+ * Returns true if the rotation handle should be showing.
+ */
+mxVertexHandler.prototype.isRotationHandleVisible = function()
+{
+	return this.graph.isEnabled() && this.rotationEnabled && this.graph.isCellRotatable(this.state.cell) &&
+		(mxGraphHandler.prototype.maxCells <= 0 || this.graph.getSelectionCount() < mxGraphHandler.prototype.maxCells) &&
+		this.state.width >= 2 && this.state.height >= 2;
+};
+
+/**
+ * Function: isConstrainedEvent
+ * 
+ * Returns true if the aspect ratio if the cell should be maintained.
+ */
+mxVertexHandler.prototype.isConstrainedEvent = function(me)
+{
+	return mxEvent.isShiftDown(me.getEvent()) || this.state.style[mxConstants.STYLE_ASPECT] == 'fixed';
+};
+
+/**
+ * Function: isCenteredEvent
+ * 
+ * Returns true if the center of the vertex should be maintained during the resize.
+ */
+mxVertexHandler.prototype.isCenteredEvent = function(state, me)
+{
+	return false;
+};
+
+/**
+ * Function: createCustomHandles
+ * 
+ * Returns an array of custom handles. This implementation returns null.
+ */
+mxVertexHandler.prototype.createCustomHandles = function()
+{
+	return null;
+};
+
+/**
+ * Function: updateMinBounds
+ * 
+ * Initializes the shapes required for this vertex handler.
+ */
+mxVertexHandler.prototype.updateMinBounds = function()
+{
+	var children = this.graph.getChildCells(this.state.cell);
+	
+	if (children.length > 0)
+	{
+		this.minBounds = this.graph.view.getBounds(children);
+		
+		if (this.minBounds != null)
+		{
+			var s = this.state.view.scale;
+			var t = this.state.view.translate;
+
+			this.minBounds.x -= this.state.x;
+			this.minBounds.y -= this.state.y;
+			this.minBounds.x /= s;
+			this.minBounds.y /= s;
+			this.minBounds.width /= s;
+			this.minBounds.height /= s;
+			this.x0 = this.state.x / s - t.x;
+			this.y0 = this.state.y / s - t.y;
+		}
+	}
+};
+
+/**
+ * Function: getSelectionBounds
+ * 
+ * Returns the mxRectangle that defines the bounds of the selection
+ * border.
+ */
+mxVertexHandler.prototype.getSelectionBounds = function(state)
+{
+	return new mxRectangle(Math.round(state.x), Math.round(state.y), Math.round(state.width), Math.round(state.height));
+};
+
+/**
+ * Function: createParentHighlightShape
+ * 
+ * Creates the shape used to draw the selection border.
+ */
+mxVertexHandler.prototype.createParentHighlightShape = function(bounds)
+{
+	return this.createSelectionShape(bounds);
+};
+
+/**
+ * Function: createSelectionShape
+ * 
+ * Creates the shape used to draw the selection border.
+ */
+mxVertexHandler.prototype.createSelectionShape = function(bounds)
+{
+	var shape = new mxRectangleShape(bounds, null, this.getSelectionColor());
+	shape.strokewidth = this.getSelectionStrokeWidth();
+	shape.isDashed = this.isSelectionDashed();
+	
+	return shape;
+};
+
+/**
+ * Function: getSelectionColor
+ * 
+ * Returns <mxConstants.VERTEX_SELECTION_COLOR>.
+ */
+mxVertexHandler.prototype.getSelectionColor = function()
+{
+	return mxConstants.VERTEX_SELECTION_COLOR;
+};
+
+/**
+ * Function: getSelectionStrokeWidth
+ * 
+ * Returns <mxConstants.VERTEX_SELECTION_STROKEWIDTH>.
+ */
+mxVertexHandler.prototype.getSelectionStrokeWidth = function()
+{
+	return mxConstants.VERTEX_SELECTION_STROKEWIDTH;
+};
+
+/**
+ * Function: isSelectionDashed
+ * 
+ * Returns <mxConstants.VERTEX_SELECTION_DASHED>.
+ */
+mxVertexHandler.prototype.isSelectionDashed = function()
+{
+	return mxConstants.VERTEX_SELECTION_DASHED;
+};
+
+/**
+ * Function: createSizer
+ * 
+ * Creates a sizer handle for the specified cursor and index and returns
+ * the new <mxRectangleShape> that represents the handle.
+ */
+mxVertexHandler.prototype.createSizer = function(cursor, index, size, fillColor)
+{
+	size = size || mxConstants.HANDLE_SIZE;
+	
+	var bounds = new mxRectangle(0, 0, size, size);
+	var sizer = this.createSizerShape(bounds, index, fillColor);
+
+	if (sizer.isHtmlAllowed() && this.state.text != null && this.state.text.node.parentNode == this.graph.container)
+	{
+		sizer.bounds.height -= 1;
+		sizer.bounds.width -= 1;
+		sizer.dialect = mxConstants.DIALECT_STRICTHTML;
+		sizer.init(this.graph.container);
+	}
+	else
+	{
+		sizer.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+				mxConstants.DIALECT_MIXEDHTML : mxConstants.DIALECT_SVG;
+		sizer.init(this.graph.getView().getOverlayPane());
+	}
+
+	mxEvent.redirectMouseEvents(sizer.node, this.graph, this.state);
+	
+	if (this.graph.isEnabled())
+	{
+		sizer.setCursor(cursor);
+	}
+	
+	if (!this.isSizerVisible(index))
+	{
+		sizer.visible = false;
+	}
+	
+	return sizer;
+};
+
+/**
+ * Function: isSizerVisible
+ * 
+ * Returns true if the sizer for the given index is visible.
+ * This returns true for all given indices.
+ */
+mxVertexHandler.prototype.isSizerVisible = function(index)
+{
+	return true;
+};
+
+/**
+ * Function: createSizerShape
+ * 
+ * Creates the shape used for the sizer handle for the specified bounds an
+ * index. Only images and rectangles should be returned if support for HTML
+ * labels with not foreign objects is required.
+ */
+mxVertexHandler.prototype.createSizerShape = function(bounds, index, fillColor)
+{
+	if (this.handleImage != null)
+	{
+		bounds = new mxRectangle(bounds.x, bounds.y, this.handleImage.width, this.handleImage.height);
+		var shape = new mxImageShape(bounds, this.handleImage.src);
+		
+		// Allows HTML rendering of the images
+		shape.preserveImageAspect = false;
+
+		return shape;
+	}
+	else if (index == mxEvent.ROTATION_HANDLE)
+	{
+		return new mxEllipse(bounds, fillColor || mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
+	}
+	else
+	{
+		return new mxRectangleShape(bounds, fillColor || mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
+	}
+};
+
+/**
+ * Function: createBounds
+ * 
+ * Helper method to create an <mxRectangle> around the given centerpoint
+ * with a width and height of 2*s or 6, if no s is given.
+ */
+mxVertexHandler.prototype.moveSizerTo = function(shape, x, y)
+{
+	if (shape != null)
+	{
+		shape.bounds.x = Math.floor(x - shape.bounds.width / 2);
+		shape.bounds.y = Math.floor(y - shape.bounds.height / 2);
+		
+		// Fixes visible inactive handles in VML
+		if (shape.node != null && shape.node.style.display != 'none')
+		{
+			shape.redraw();
+		}
+	}
+};
+
+/**
+ * Function: getHandleForEvent
+ * 
+ * Returns the index of the handle for the given event. This returns the index
+ * of the sizer from where the event originated or <mxEvent.LABEL_INDEX>.
+ */
+mxVertexHandler.prototype.getHandleForEvent = function(me)
+{
+	// Connection highlight may consume events before they reach sizer handle
+	var tol = (!mxEvent.isMouseEvent(me.getEvent())) ? this.tolerance : 1;
+	var hit = (this.allowHandleBoundsCheck && (mxClient.IS_IE || tol > 0)) ?
+		new mxRectangle(me.getGraphX() - tol, me.getGraphY() - tol, 2 * tol, 2 * tol) : null;
+	
+	function checkShape(shape)
+	{
+		return shape != null && (me.isSource(shape) || (hit != null && mxUtils.intersects(shape.bounds, hit) &&
+			shape.node.style.display != 'none' && shape.node.style.visibility != 'hidden'));
+	}
+
+	if (this.customHandles != null && this.isCustomHandleEvent(me))
+	{
+		// Inverse loop order to match display order
+		for (var i = this.customHandles.length - 1; i >= 0; i--)
+		{
+			if (checkShape(this.customHandles[i].shape))
+			{
+				// LATER: Return reference to active shape
+				return mxEvent.CUSTOM_HANDLE - i;
+			}
+		}
+	}
+
+	if (checkShape(this.rotationShape))
+	{
+		return mxEvent.ROTATION_HANDLE;
+	}
+	else if (checkShape(this.labelShape))
+	{
+		return mxEvent.LABEL_HANDLE;
+	}
+	
+	if (this.sizers != null)
+	{
+		for (var i = 0; i < this.sizers.length; i++)
+		{
+			if (checkShape(this.sizers[i]))
+			{
+				return i;
+			}
+		}
+	}
+
+	return null;
+};
+
+/**
+ * Function: isCustomHandleEvent
+ * 
+ * Returns true if the given event allows custom handles to be changed. This
+ * implementation returns true.
+ */
+mxVertexHandler.prototype.isCustomHandleEvent = function(me)
+{
+	return true;
+};
+
+/**
+ * Function: mouseDown
+ * 
+ * Handles the event if a handle has been clicked. By consuming the
+ * event all subsequent events of the gesture are redirected to this
+ * handler.
+ */
+mxVertexHandler.prototype.mouseDown = function(sender, me)
+{
+	var tol = (!mxEvent.isMouseEvent(me.getEvent())) ? this.tolerance : 0;
+	
+	if (!me.isConsumed() && this.graph.isEnabled() && (tol > 0 || me.getState() == this.state))
+	{
+		var handle = this.getHandleForEvent(me);
+
+		if (handle != null)
+		{
+			this.start(me.getGraphX(), me.getGraphY(), handle);
+			me.consume();
+		}
+	}
+};
+
+/**
+ * Function: isLivePreviewBorder
+ * 
+ * Called if <livePreview> is enabled to check if a border should be painted.
+ * This implementation returns true if the shape is transparent.
+ */
+mxVertexHandler.prototype.isLivePreviewBorder = function()
+{
+	return this.state.shape != null && this.state.shape.fill == null && this.state.shape.stroke == null;
+};
+
+/**
+ * Function: start
+ * 
+ * Starts the handling of the mouse gesture.
+ */
+mxVertexHandler.prototype.start = function(x, y, index)
+{
+	this.inTolerance = true;
+	this.childOffsetX = 0;
+	this.childOffsetY = 0;
+	this.index = index;
+	this.startX = x;
+	this.startY = y;
+	
+	// Saves reference to parent state
+	var model = this.state.view.graph.model;
+	var parent = model.getParent(this.state.cell);
+	
+	if (this.state.view.currentRoot != parent && (model.isVertex(parent) || model.isEdge(parent)))
+	{
+		this.parentState = this.state.view.graph.view.getState(parent);
+	}
+	
+	// Creates a preview that can be on top of any HTML label
+	this.selectionBorder.node.style.display = (index == mxEvent.ROTATION_HANDLE) ? 'inline' : 'none';
+	
+	// Creates the border that represents the new bounds
+	if (!this.livePreview || this.isLivePreviewBorder())
+	{
+		this.preview = this.createSelectionShape(this.bounds);
+		
+		if (!(mxClient.IS_SVG && Number(this.state.style[mxConstants.STYLE_ROTATION] || '0') != 0) &&
+			this.state.text != null && this.state.text.node.parentNode == this.graph.container)
+		{
+			this.preview.dialect = mxConstants.DIALECT_STRICTHTML;
+			this.preview.init(this.graph.container);
+		}
+		else
+		{
+			this.preview.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
+					mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+			this.preview.init(this.graph.view.getOverlayPane());
+		}
+	}
+	
+	// Prepares the handles for live preview
+	if (this.livePreview)
+	{
+		this.hideSizers();
+		
+		if (index == mxEvent.ROTATION_HANDLE)
+		{
+			this.rotationShape.node.style.display = '';
+		}
+		else if (index == mxEvent.LABEL_HANDLE)
+		{
+			this.labelShape.node.style.display = '';
+		}
+		else if (this.sizers != null && this.sizers[index] != null)
+		{
+			this.sizers[index].node.style.display = '';
+		}
+		else if (index <= mxEvent.CUSTOM_HANDLE && this.customHandles != null)
+		{
+			this.customHandles[mxEvent.CUSTOM_HANDLE - index].setVisible(true);
+		}
+		
+		// Gets the array of connected edge handlers for redrawing
+		var edges = this.graph.getEdges(this.state.cell);
+		this.edgeHandlers = [];
+		
+		for (var i = 0; i < edges.length; i++)
+		{
+			var handler = this.graph.selectionCellsHandler.getHandler(edges[i]);
+			
+			if (handler != null)
+			{
+				this.edgeHandlers.push(handler);
+			}
+		}
+	}
+};
+
+/**
+ * Function: hideHandles
+ * 
+ * Shortcut to <hideSizers>.
+ */
+mxVertexHandler.prototype.setHandlesVisible = function(visible)
+{
+	if (this.sizers != null)
+	{
+		for (var i = 0; i < this.sizers.length; i++)
+		{
+			this.sizers[i].node.style.display = (visible) ? '' : 'none';
+		}
+	}
+
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			this.customHandles[i].setVisible(visible);
+		}
+	}
+};
+
+/**
+ * Function: hideSizers
+ * 
+ * Hides all sizers except.
+ * 
+ * Starts the handling of the mouse gesture.
+ */
+mxVertexHandler.prototype.hideSizers = function()
+{
+	this.setHandlesVisible(false);
+};
+
+/**
+ * Function: checkTolerance
+ * 
+ * Checks if the coordinates for the given event are within the
+ * <mxGraph.tolerance>. If the event is a mouse event then the tolerance is
+ * ignored.
+ */
+mxVertexHandler.prototype.checkTolerance = function(me)
+{
+	if (this.inTolerance && this.startX != null && this.startY != null)
+	{
+		if (mxEvent.isMouseEvent(me.getEvent()) ||
+			Math.abs(me.getGraphX() - this.startX) > this.graph.tolerance ||
+			Math.abs(me.getGraphY() - this.startY) > this.graph.tolerance)
+		{
+			this.inTolerance = false;
+		}
+	}
+};
+
+/**
+ * Function: updateHint
+ * 
+ * Hook for subclassers do show details while the handler is active.
+ */
+mxVertexHandler.prototype.updateHint = function(me) { };
+
+/**
+ * Function: removeHint
+ * 
+ * Hooks for subclassers to hide details when the handler gets inactive.
+ */
+mxVertexHandler.prototype.removeHint = function() { };
+
+/**
+ * Function: roundAngle
+ * 
+ * Hook for rounding the angle. This uses Math.round.
+ */
+mxVertexHandler.prototype.roundAngle = function(angle)
+{
+	return Math.round(angle * 10) / 10;
+};
+
+/**
+ * Function: roundLength
+ * 
+ * Hook for rounding the unscaled width or height. This uses Math.round.
+ */
+mxVertexHandler.prototype.roundLength = function(length)
+{
+	return Math.round(length);
+};
+
+/**
+ * Function: mouseMove
+ * 
+ * Handles the event by updating the preview.
+ */
+mxVertexHandler.prototype.mouseMove = function(sender, me)
+{
+	if (!me.isConsumed() && this.index != null)
+	{
+		// Checks tolerance for ignoring single clicks
+		this.checkTolerance(me);
+
+		if (!this.inTolerance)
+		{
+			if (this.index <= mxEvent.CUSTOM_HANDLE)
+			{
+				if (this.customHandles != null)
+				{
+					this.customHandles[mxEvent.CUSTOM_HANDLE - this.index].processEvent(me);
+					this.customHandles[mxEvent.CUSTOM_HANDLE - this.index].active = true;
+				}
+			}
+			else if (this.index == mxEvent.LABEL_HANDLE)
+			{
+				this.moveLabel(me);
+			}
+			else if (this.index == mxEvent.ROTATION_HANDLE)
+			{
+				this.rotateVertex(me);
+			}
+			else
+			{
+				this.resizeVertex(me);
+			}
+
+			this.updateHint(me);
+		}
+		
+		me.consume();
+	}
+	// Workaround for disabling the connect highlight when over handle
+	else if (!this.graph.isMouseDown && this.getHandleForEvent(me) != null)
+	{
+		me.consume(false);
+	}
+};
+
+/**
+ * Function: rotateVertex
+ * 
+ * Rotates the vertex.
+ */
+mxVertexHandler.prototype.moveLabel = function(me)
+{
+	var point = new mxPoint(me.getGraphX(), me.getGraphY());
+	var tr = this.graph.view.translate;
+	var scale = this.graph.view.scale;
+	
+	if (this.graph.isGridEnabledEvent(me.getEvent()))
+	{
+		point.x = (this.graph.snap(point.x / scale - tr.x) + tr.x) * scale;
+		point.y = (this.graph.snap(point.y / scale - tr.y) + tr.y) * scale;
+	}
+
+	var index = (this.rotationShape != null) ? this.sizers.length - 2 : this.sizers.length - 1;
+	this.moveSizerTo(this.sizers[index], point.x, point.y);
+};
+
+/**
+ * Function: rotateVertex
+ * 
+ * Rotates the vertex.
+ */
+mxVertexHandler.prototype.rotateVertex = function(me)
+{
+	var point = new mxPoint(me.getGraphX(), me.getGraphY());
+	var dx = this.state.x + this.state.width / 2 - point.x;
+	var dy = this.state.y + this.state.height / 2 - point.y;
+	this.currentAlpha = (dx != 0) ? Math.atan(dy / dx) * 180 / Math.PI + 90 : ((dy < 0) ? 180 : 0);
+	
+	if (dx > 0)
+	{
+		this.currentAlpha -= 180;
+	}
+
+	// Rotation raster
+	if (this.rotationRaster && this.graph.isGridEnabledEvent(me.getEvent()))
+	{
+		var dx = point.x - this.state.getCenterX();
+		var dy = point.y - this.state.getCenterY();
+		var dist = Math.abs(Math.sqrt(dx * dx + dy * dy) - 20) * 3;
+		var raster = Math.max(1, 5 * Math.min(3, Math.max(0, Math.round(80 / Math.abs(dist)))));
+		
+		this.currentAlpha = Math.round(this.currentAlpha / raster) * raster;
+	}
+	else
+	{
+		this.currentAlpha = this.roundAngle(this.currentAlpha);
+	}
+
+	this.selectionBorder.rotation = this.currentAlpha;
+	this.selectionBorder.redraw();
+					
+	if (this.livePreview)
+	{
+		this.redrawHandles();
+	}
+};
+
+/**
+ * Function: rotateVertex
+ * 
+ * Rotates the vertex.
+ */
+mxVertexHandler.prototype.resizeVertex = function(me)
+{
+	var ct = new mxPoint(this.state.getCenterX(), this.state.getCenterY());
+	var alpha = mxUtils.toRadians(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+	var point = new mxPoint(me.getGraphX(), me.getGraphY());
+	var tr = this.graph.view.translate;
+	var scale = this.graph.view.scale;
+	var cos = Math.cos(-alpha);
+	var sin = Math.sin(-alpha);
+	
+	var dx = point.x - this.startX;
+	var dy = point.y - this.startY;
+
+	// Rotates vector for mouse gesture
+	var tx = cos * dx - sin * dy;
+	var ty = sin * dx + cos * dy;
+	
+	dx = tx;
+	dy = ty;
+
+	var geo = this.graph.getCellGeometry(this.state.cell);
+	this.unscaledBounds = this.union(geo, dx / scale, dy / scale, this.index,
+		this.graph.isGridEnabledEvent(me.getEvent()), 1,
+		new mxPoint(0, 0), this.isConstrainedEvent(me),
+		this.isCenteredEvent(this.state, me));
+	
+	// Keeps vertex within maximum graph or parent bounds
+	if (!geo.relative)
+	{
+		var max = this.graph.getMaximumGraphBounds();
+		
+		// Handles child cells
+		if (max != null && this.parentState != null)
+		{
+			max = mxRectangle.fromRectangle(max);
+			
+			max.x -= (this.parentState.x - tr.x * scale) / scale;
+			max.y -= (this.parentState.y - tr.y * scale) / scale;
+		}
+		
+		if (this.graph.isConstrainChild(this.state.cell))
+		{
+			var tmp = this.graph.getCellContainmentArea(this.state.cell);
+			
+			if (tmp != null)
+			{
+				var overlap = this.graph.getOverlap(this.state.cell);
+				
+				if (overlap > 0)
+				{
+					tmp = mxRectangle.fromRectangle(tmp);
+					
+					tmp.x -= tmp.width * overlap;
+					tmp.y -= tmp.height * overlap;
+					tmp.width += 2 * tmp.width * overlap;
+					tmp.height += 2 * tmp.height * overlap;
+				}
+				
+				if (max == null)
+				{
+					max = tmp;
+				}
+				else
+				{
+					max = mxRectangle.fromRectangle(max);
+					max.intersect(tmp);
+				}
+			}
+		}
+	
+		if (max != null)
+		{
+			if (this.unscaledBounds.x < max.x)
+			{
+				this.unscaledBounds.width -= max.x - this.unscaledBounds.x;
+				this.unscaledBounds.x = max.x;
+			}
+			
+			if (this.unscaledBounds.y < max.y)
+			{
+				this.unscaledBounds.height -= max.y - this.unscaledBounds.y;
+				this.unscaledBounds.y = max.y;
+			}
+			
+			if (this.unscaledBounds.x + this.unscaledBounds.width > max.x + max.width)
+			{
+				this.unscaledBounds.width -= this.unscaledBounds.x +
+					this.unscaledBounds.width - max.x - max.width;
+			}
+			
+			if (this.unscaledBounds.y + this.unscaledBounds.height > max.y + max.height)
+			{
+				this.unscaledBounds.height -= this.unscaledBounds.y +
+					this.unscaledBounds.height - max.y - max.height;
+			}
+		}
+	}
+	
+	this.bounds = new mxRectangle(((this.parentState != null) ? this.parentState.x : tr.x * scale) +
+		(this.unscaledBounds.x) * scale, ((this.parentState != null) ? this.parentState.y : tr.y * scale) +
+		(this.unscaledBounds.y) * scale, this.unscaledBounds.width * scale, this.unscaledBounds.height * scale);
+
+	if (geo.relative && this.parentState != null)
+	{
+		this.bounds.x += this.state.x - this.parentState.x;
+		this.bounds.y += this.state.y - this.parentState.y;
+	}
+
+	cos = Math.cos(alpha);
+	sin = Math.sin(alpha);
+	
+	var c2 = new mxPoint(this.bounds.getCenterX(), this.bounds.getCenterY());
+
+	var dx = c2.x - ct.x;
+	var dy = c2.y - ct.y;
+	
+	var dx2 = cos * dx - sin * dy;
+	var dy2 = sin * dx + cos * dy;
+	
+	var dx3 = dx2 - dx;
+	var dy3 = dy2 - dy;
+	
+	var dx4 = this.bounds.x - this.state.x;
+	var dy4 = this.bounds.y - this.state.y;
+	
+	var dx5 = cos * dx4 - sin * dy4;
+	var dy5 = sin * dx4 + cos * dy4;
+	
+	this.bounds.x += dx3;
+	this.bounds.y += dy3;
+	
+	// Rounds unscaled bounds to int
+	this.unscaledBounds.x = this.roundLength(this.unscaledBounds.x + dx3 / scale);
+	this.unscaledBounds.y = this.roundLength(this.unscaledBounds.y + dy3 / scale);
+	this.unscaledBounds.width = this.roundLength(this.unscaledBounds.width);
+	this.unscaledBounds.height = this.roundLength(this.unscaledBounds.height);
+	
+	// Shifts the children according to parent offset
+	if (!this.graph.isCellCollapsed(this.state.cell) && (dx3 != 0 || dy3 != 0))
+	{
+		this.childOffsetX = this.state.x - this.bounds.x + dx5;
+		this.childOffsetY = this.state.y - this.bounds.y + dy5;
+	}
+	else
+	{
+		this.childOffsetX = 0;
+		this.childOffsetY = 0;
+	}
+	
+	if (this.livePreview)
+	{
+		this.updateLivePreview(me);
+	}
+	
+	if (this.preview != null)
+	{
+		this.drawPreview();
+	}
+};
+
+/**
+ * Function: updateLivePreview
+ * 
+ * Repaints the live preview.
+ */
+mxVertexHandler.prototype.updateLivePreview = function(me)
+{
+	// TODO: Apply child offset to children in live preview
+	var scale = this.graph.view.scale;
+	var tr = this.graph.view.translate;
+	
+	// Saves current state
+	var tempState = this.state.clone();
+
+	// Temporarily changes size and origin
+	this.state.x = this.bounds.x;
+	this.state.y = this.bounds.y;
+	this.state.origin = new mxPoint(this.state.x / scale - tr.x, this.state.y / scale - tr.y);
+	this.state.width = this.bounds.width;
+	this.state.height = this.bounds.height;
+	
+	// Needed to force update of text bounds
+	this.state.unscaledWidth = null;
+	
+	// Redraws cell and handles
+	var off = this.state.absoluteOffset;
+	off = new mxPoint(off.x, off.y);
+
+	// Required to store and reset absolute offset for updating label position
+	this.state.absoluteOffset.x = 0;
+	this.state.absoluteOffset.y = 0;
+	var geo = this.graph.getCellGeometry(this.state.cell);				
+
+	if (geo != null)
+	{
+		var offset = geo.offset || this.EMPTY_POINT;
+
+		if (offset != null && !geo.relative)
+		{
+			this.state.absoluteOffset.x = this.state.view.scale * offset.x;
+			this.state.absoluteOffset.y = this.state.view.scale * offset.y;
+		}
+		
+		this.state.view.updateVertexLabelOffset(this.state);
+	}
+	
+	// Draws the live preview
+	this.state.view.graph.cellRenderer.redraw(this.state, true);
+	
+	// Redraws connected edges TODO: Include child edges
+	this.state.view.invalidate(this.state.cell);
+	this.state.invalid = false;
+	this.state.view.validate();
+	this.redrawHandles();
+	
+	// Restores current state
+	this.state.setState(tempState);
+};
+
+/**
+ * Function: mouseUp
+ * 
+ * Handles the event by applying the changes to the geometry.
+ */
+mxVertexHandler.prototype.mouseUp = function(sender, me)
+{
+	if (this.index != null && this.state != null)
+	{
+		var point = new mxPoint(me.getGraphX(), me.getGraphY());
+
+		this.graph.getModel().beginUpdate();
+		try
+		{
+			if (this.index <= mxEvent.CUSTOM_HANDLE)
+			{
+				if (this.customHandles != null)
+				{
+					this.customHandles[mxEvent.CUSTOM_HANDLE - this.index].active = false;
+					this.customHandles[mxEvent.CUSTOM_HANDLE - this.index].execute();
+				}
+			}
+			else if (this.index == mxEvent.ROTATION_HANDLE)
+			{
+				if (this.currentAlpha != null)
+				{
+					var delta = this.currentAlpha - (this.state.style[mxConstants.STYLE_ROTATION] || 0);
+					
+					if (delta != 0)
+					{
+						this.rotateCell(this.state.cell, delta);
+					}
+				}
+				else
+				{
+					this.rotateClick();
+				}
+			}
+			else
+			{
+				var gridEnabled = this.graph.isGridEnabledEvent(me.getEvent());
+				var alpha = mxUtils.toRadians(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+				var cos = Math.cos(-alpha);
+				var sin = Math.sin(-alpha);
+				
+				var dx = point.x - this.startX;
+				var dy = point.y - this.startY;
+				
+				// Rotates vector for mouse gesture
+				var tx = cos * dx - sin * dy;
+				var ty = sin * dx + cos * dy;
+				
+				dx = tx;
+				dy = ty;
+				
+				var s = this.graph.view.scale;
+				var recurse = this.isRecursiveResize(this.state, me);
+				this.resizeCell(this.state.cell, this.roundLength(dx / s), this.roundLength(dy / s),
+					this.index, gridEnabled, this.isConstrainedEvent(me), recurse);
+			}
+		}
+		finally
+		{
+			this.graph.getModel().endUpdate();
+		}
+
+		me.consume();
+		this.reset();
+	}
+};
+
+/**
+ * Function: rotateCell
+ * 
+ * Rotates the given cell to the given rotation.
+ */
+mxVertexHandler.prototype.isRecursiveResize = function(state, me)
+{
+	return this.graph.isRecursiveResize(this.state);
+};
+
+/**
+ * Function: rotateClick
+ * 
+ * Hook for subclassers to implement a single click on the rotation handle.
+ * This code is executed as part of the model transaction. This implementation
+ * is empty.
+ */
+mxVertexHandler.prototype.rotateClick = function() { };
+
+/**
+ * Function: rotateCell
+ * 
+ * Rotates the given cell and its children by the given angle in degrees.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> to be rotated.
+ * angle - Angle in degrees.
+ */
+mxVertexHandler.prototype.rotateCell = function(cell, angle, parent)
+{
+	if (angle != 0)
+	{
+		var model = this.graph.getModel();
+
+		if (model.isVertex(cell) || model.isEdge(cell))
+		{
+			if (!model.isEdge(cell))
+			{
+				var state = this.graph.view.getState(cell);
+				var style = (state != null) ? state.style : this.graph.getCellStyle(cell);
+		
+				if (style != null)
+				{
+					var total = (style[mxConstants.STYLE_ROTATION] || 0) + angle;
+					this.graph.setCellStyles(mxConstants.STYLE_ROTATION, total, [cell]);
+				}
+			}
+			
+			var geo = this.graph.getCellGeometry(cell);
+			
+			if (geo != null)
+			{
+				var pgeo = this.graph.getCellGeometry(parent);
+				
+				if (pgeo != null && !model.isEdge(parent))
+				{
+					geo = geo.clone();
+					geo.rotate(angle, new mxPoint(pgeo.width / 2, pgeo.height / 2));
+					model.setGeometry(cell, geo);
+				}
+				
+				if ((model.isVertex(cell) && !geo.relative) || model.isEdge(cell))
+				{
+					// Recursive rotation
+					var childCount = model.getChildCount(cell);
+					
+					for (var i = 0; i < childCount; i++)
+					{
+						this.rotateCell(model.getChildAt(cell, i), angle, cell);
+					}
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: reset
+ * 
+ * Resets the state of this handler.
+ */
+mxVertexHandler.prototype.reset = function()
+{
+	if (this.sizers != null && this.index != null && this.sizers[this.index] != null &&
+		this.sizers[this.index].node.style.display == 'none')
+	{
+		this.sizers[this.index].node.style.display = '';
+	}
+
+	this.currentAlpha = null;
+	this.inTolerance = null;
+	this.index = null;
+
+	// TODO: Reset and redraw cell states for live preview
+	if (this.preview != null)
+	{
+		this.preview.destroy();
+		this.preview = null;
+	}
+
+	if (this.livePreview && this.sizers != null)
+	{
+		for (var i = 0; i < this.sizers.length; i++)
+		{
+			if (this.sizers[i] != null)
+			{
+				this.sizers[i].node.style.display = '';
+			}
+		}
+	}
+
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			if (this.customHandles[i].active)
+			{
+				this.customHandles[i].active = false;
+				this.customHandles[i].reset();
+			}
+			else
+			{
+				this.customHandles[i].setVisible(true);
+			}
+		}
+	}
+	
+	// Checks if handler has been destroyed
+	if (this.selectionBorder != null)
+	{
+		this.selectionBorder.node.style.display = 'inline';
+		this.selectionBounds = this.getSelectionBounds(this.state);
+		this.bounds = new mxRectangle(this.selectionBounds.x, this.selectionBounds.y,
+			this.selectionBounds.width, this.selectionBounds.height);
+		this.drawPreview();
+	}
+
+	this.removeHint();
+	this.redrawHandles();
+	this.edgeHandlers = null;
+	this.unscaledBounds = null;
+};
+
+/**
+ * Function: resizeCell
+ * 
+ * Uses the given vector to change the bounds of the given cell
+ * in the graph using <mxGraph.resizeCell>.
+ */
+mxVertexHandler.prototype.resizeCell = function(cell, dx, dy, index, gridEnabled, constrained, recurse)
+{
+	var geo = this.graph.model.getGeometry(cell);
+	
+	if (geo != null)
+	{
+		if (index == mxEvent.LABEL_HANDLE)
+		{
+			var scale = this.graph.view.scale;
+			dx = Math.round((this.labelShape.bounds.getCenterX() - this.startX) / scale);
+			dy = Math.round((this.labelShape.bounds.getCenterY() - this.startY) / scale);
+			
+			geo = geo.clone();
+			
+			if (geo.offset == null)
+			{
+				geo.offset = new mxPoint(dx, dy);
+			}
+			else
+			{
+				geo.offset.x += dx;
+				geo.offset.y += dy;
+			}
+			
+			this.graph.model.setGeometry(cell, geo);
+		}
+		else if (this.unscaledBounds != null)
+		{
+			var scale = this.graph.view.scale;
+
+			if (this.childOffsetX != 0 || this.childOffsetY != 0)
+			{
+				this.moveChildren(cell, Math.round(this.childOffsetX / scale), Math.round(this.childOffsetY / scale));
+			}
+
+			this.graph.resizeCell(cell, this.unscaledBounds, recurse);
+		}
+	}
+};
+
+/**
+ * Function: moveChildren
+ * 
+ * Moves the children of the given cell by the given vector.
+ */
+mxVertexHandler.prototype.moveChildren = function(cell, dx, dy)
+{
+	var model = this.graph.getModel();
+	var childCount = model.getChildCount(cell);
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		var child = model.getChildAt(cell, i);
+		var geo = this.graph.getCellGeometry(child);
+		
+		if (geo != null)
+		{
+			geo = geo.clone();
+			geo.translate(dx, dy);
+			model.setGeometry(child, geo);
+		}
+	}
+};
+/**
+ * Function: union
+ * 
+ * Returns the union of the given bounds and location for the specified
+ * handle index.
+ * 
+ * To override this to limit the size of vertex via a minWidth/-Height style,
+ * the following code can be used.
+ * 
+ * (code)
+ * var vertexHandlerUnion = mxVertexHandler.prototype.union;
+ * mxVertexHandler.prototype.union = function(bounds, dx, dy, index, gridEnabled, scale, tr, constrained)
+ * {
+ *   var result = vertexHandlerUnion.apply(this, arguments);
+ *   
+ *   result.width = Math.max(result.width, mxUtils.getNumber(this.state.style, 'minWidth', 0));
+ *   result.height = Math.max(result.height, mxUtils.getNumber(this.state.style, 'minHeight', 0));
+ *   
+ *   return result;
+ * };
+ * (end)
+ * 
+ * The minWidth/-Height style can then be used as follows:
+ * 
+ * (code)
+ * graph.insertVertex(parent, null, 'Hello,', 20, 20, 80, 30, 'minWidth=100;minHeight=100;');
+ * (end)
+ * 
+ * To override this to update the height for a wrapped text if the width of a vertex is
+ * changed, the following can be used.
+ * 
+ * (code)
+ * var mxVertexHandlerUnion = mxVertexHandler.prototype.union;
+ * mxVertexHandler.prototype.union = function(bounds, dx, dy, index, gridEnabled, scale, tr, constrained)
+ * {
+ *   var result = mxVertexHandlerUnion.apply(this, arguments);
+ *   var s = this.state;
+ *   
+ *   if (this.graph.isHtmlLabel(s.cell) && (index == 3 || index == 4) &&
+ *       s.text != null && s.style[mxConstants.STYLE_WHITE_SPACE] == 'wrap')
+ *   {
+ *     var label = this.graph.getLabel(s.cell);
+ *     var fontSize = mxUtils.getNumber(s.style, mxConstants.STYLE_FONTSIZE, mxConstants.DEFAULT_FONTSIZE);
+ *     var ww = result.width / s.view.scale - s.text.spacingRight - s.text.spacingLeft
+ *     
+ *     result.height = mxUtils.getSizeForString(label, fontSize, s.style[mxConstants.STYLE_FONTFAMILY], ww).height;
+ *   }
+ *   
+ *   return result;
+ * };
+ * (end)
+ */
+mxVertexHandler.prototype.union = function(bounds, dx, dy, index, gridEnabled, scale, tr, constrained, centered)
+{
+	if (this.singleSizer)
+	{
+		var x = bounds.x + bounds.width + dx;
+		var y = bounds.y + bounds.height + dy;
+		
+		if (gridEnabled)
+		{
+			x = this.graph.snap(x / scale) * scale;
+			y = this.graph.snap(y / scale) * scale;
+		}
+		
+		var rect = new mxRectangle(bounds.x, bounds.y, 0, 0);
+		rect.add(new mxRectangle(x, y, 0, 0));
+		
+		return rect;
+	}
+	else
+	{
+		var w0 = bounds.width;
+		var h0 = bounds.height;
+		var left = bounds.x - tr.x * scale;
+		var right = left + w0;
+		var top = bounds.y - tr.y * scale;
+		var bottom = top + h0;
+		
+		var cx = left + w0 / 2;
+		var cy = top + h0 / 2;
+		
+		if (index > 4 /* Bottom Row */)
+		{
+			bottom = bottom + dy;
+			
+			if (gridEnabled)
+			{
+				bottom = this.graph.snap(bottom / scale) * scale;
+			}
+		}
+		else if (index < 3 /* Top Row */)
+		{
+			top = top + dy;
+			
+			if (gridEnabled)
+			{
+				top = this.graph.snap(top / scale) * scale;
+			}
+		}
+		
+		if (index == 0 || index == 3 || index == 5 /* Left */)
+		{
+			left += dx;
+			
+			if (gridEnabled)
+			{
+				left = this.graph.snap(left / scale) * scale;
+			}
+		}
+		else if (index == 2 || index == 4 || index == 7 /* Right */)
+		{
+			right += dx;
+			
+			if (gridEnabled)
+			{
+				right = this.graph.snap(right / scale) * scale;
+			}
+		}
+		
+		var width = right - left;
+		var height = bottom - top;
+
+		if (constrained)
+		{
+			var geo = this.graph.getCellGeometry(this.state.cell);
+
+			if (geo != null)
+			{
+				var aspect = geo.width / geo.height;
+				
+				if (index== 1 || index== 2 || index == 7 || index == 6)
+				{
+					width = height * aspect;
+				}
+				else
+				{
+					height = width / aspect;
+				}
+				
+				if (index == 0)
+				{
+					left = right - width;
+					top = bottom - height;
+				}
+			}
+		}
+
+		if (centered)
+		{
+			width += (width - w0);
+			height += (height - h0);
+			
+			var cdx = cx - (left + width / 2);
+			var cdy = cy - (top + height / 2);
+
+			left += cdx;
+			top += cdy;
+			right += cdx;
+			bottom += cdy;
+		}
+
+		// Flips over left side
+		if (width < 0)
+		{
+			left += width;
+			width = Math.abs(width);
+		}
+		
+		// Flips over top side
+		if (height < 0)
+		{
+			top += height;
+			height = Math.abs(height);
+		}
+
+		var result = new mxRectangle(left + tr.x * scale, top + tr.y * scale, width, height);
+		
+		if (this.minBounds != null)
+		{
+			result.width = Math.max(result.width, this.minBounds.x * scale + this.minBounds.width * scale +
+				Math.max(0, this.x0 * scale - result.x));
+			result.height = Math.max(result.height, this.minBounds.y * scale + this.minBounds.height * scale +
+				Math.max(0, this.y0 * scale - result.y));
+		}
+		
+		return result;
+	}
+};
+
+/**
+ * Function: redraw
+ * 
+ * Redraws the handles and the preview.
+ */
+mxVertexHandler.prototype.redraw = function()
+{
+	this.selectionBounds = this.getSelectionBounds(this.state);
+	this.bounds = new mxRectangle(this.selectionBounds.x, this.selectionBounds.y, this.selectionBounds.width, this.selectionBounds.height);
+	
+	this.redrawHandles();
+	this.drawPreview();
+};
+
+/**
+ * Returns the padding to be used for drawing handles for the current <bounds>.
+ */
+mxVertexHandler.prototype.getHandlePadding = function()
+{
+	// KNOWN: Tolerance depends on event type (eg. 0 for mouse events)
+	var result = new mxPoint(0, 0);
+	var tol = this.tolerance;
+
+	if (this.sizers != null && this.sizers.length > 0 && this.sizers[0] != null &&
+		(this.bounds.width < 2 * this.sizers[0].bounds.width + 2 * tol ||
+		this.bounds.height < 2 * this.sizers[0].bounds.height + 2 * tol))
+	{
+		tol /= 2;
+		
+		result.x = this.sizers[0].bounds.width + tol;
+		result.y = this.sizers[0].bounds.height + tol;
+	}
+	
+	return result;
+};
+
+/**
+ * Function: redrawHandles
+ * 
+ * Redraws the handles. To hide certain handles the following code can be used.
+ * 
+ * (code)
+ * mxVertexHandler.prototype.redrawHandles = function()
+ * {
+ *   mxVertexHandlerRedrawHandles.apply(this, arguments);
+ *   
+ *   if (this.sizers != null && this.sizers.length > 7)
+ *   {
+ *     this.sizers[1].node.style.display = 'none';
+ *     this.sizers[6].node.style.display = 'none';
+ *   }
+ * };
+ * (end)
+ */
+mxVertexHandler.prototype.redrawHandles = function()
+{
+	var tol = this.tolerance;
+	this.horizontalOffset = 0;
+	this.verticalOffset = 0;
+	var s = this.bounds;
+
+	if (this.sizers != null && this.sizers.length > 0 && this.sizers[0] != null)
+	{
+		if (this.index == null && this.manageSizers && this.sizers.length >= 8)
+		{
+			// KNOWN: Tolerance depends on event type (eg. 0 for mouse events)
+			var padding = this.getHandlePadding();
+			this.horizontalOffset = padding.x;
+			this.verticalOffset = padding.y;
+			
+			if (this.horizontalOffset != 0 || this.verticalOffset != 0)
+			{
+				s = new mxRectangle(s.x, s.y, s.width, s.height);
+
+				s.x -= this.horizontalOffset / 2;
+				s.width += this.horizontalOffset;
+				s.y -= this.verticalOffset / 2;
+				s.height += this.verticalOffset;
+			}
+			
+			if (this.sizers.length >= 8)
+			{
+				if ((s.width < 2 * this.sizers[0].bounds.width + 2 * tol) ||
+					(s.height < 2 * this.sizers[0].bounds.height + 2 * tol))
+				{
+					this.sizers[0].node.style.display = 'none';
+					this.sizers[2].node.style.display = 'none';
+					this.sizers[5].node.style.display = 'none';
+					this.sizers[7].node.style.display = 'none';
+				}
+				else
+				{
+					this.sizers[0].node.style.display = '';
+					this.sizers[2].node.style.display = '';
+					this.sizers[5].node.style.display = '';
+					this.sizers[7].node.style.display = '';
+				}
+			}
+		}
+
+		var r = s.x + s.width;
+		var b = s.y + s.height;
+		
+		if (this.singleSizer)
+		{
+			this.moveSizerTo(this.sizers[0], r, b);
+		}
+		else
+		{
+			var cx = s.x + s.width / 2;
+			var cy = s.y + s.height / 2;
+			
+			if (this.sizers.length >= 8)
+			{
+				var crs = ['nw-resize', 'n-resize', 'ne-resize', 'e-resize', 'se-resize', 's-resize', 'sw-resize', 'w-resize'];
+				
+				var alpha = mxUtils.toRadians(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+				var cos = Math.cos(alpha);
+				var sin = Math.sin(alpha);
+				
+				var da = Math.round(alpha * 4 / Math.PI);
+				
+				var ct = new mxPoint(s.getCenterX(), s.getCenterY());
+				var pt = mxUtils.getRotatedPoint(new mxPoint(s.x, s.y), cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[0], pt.x, pt.y);
+				this.sizers[0].setCursor(crs[mxUtils.mod(0 + da, crs.length)]);
+				
+				pt.x = cx;
+				pt.y = s.y;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[1], pt.x, pt.y);
+				this.sizers[1].setCursor(crs[mxUtils.mod(1 + da, crs.length)]);
+				
+				pt.x = r;
+				pt.y = s.y;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[2], pt.x, pt.y);
+				this.sizers[2].setCursor(crs[mxUtils.mod(2 + da, crs.length)]);
+				
+				pt.x = s.x;
+				pt.y = cy;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[3], pt.x, pt.y);
+				this.sizers[3].setCursor(crs[mxUtils.mod(7 + da, crs.length)]);
+
+				pt.x = r;
+				pt.y = cy;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[4], pt.x, pt.y);
+				this.sizers[4].setCursor(crs[mxUtils.mod(3 + da, crs.length)]);
+
+				pt.x = s.x;
+				pt.y = b;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[5], pt.x, pt.y);
+				this.sizers[5].setCursor(crs[mxUtils.mod(6 + da, crs.length)]);
+
+				pt.x = cx;
+				pt.y = b;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[6], pt.x, pt.y);
+				this.sizers[6].setCursor(crs[mxUtils.mod(5 + da, crs.length)]);
+
+				pt.x = r;
+				pt.y = b;
+				pt = mxUtils.getRotatedPoint(pt, cos, sin, ct);
+				
+				this.moveSizerTo(this.sizers[7], pt.x, pt.y);
+				this.sizers[7].setCursor(crs[mxUtils.mod(4 + da, crs.length)]);
+				
+				this.moveSizerTo(this.sizers[8], cx + this.state.absoluteOffset.x, cy + this.state.absoluteOffset.y);
+			}
+			else if (this.state.width >= 2 && this.state.height >= 2)
+			{
+				this.moveSizerTo(this.sizers[0], cx + this.state.absoluteOffset.x, cy + this.state.absoluteOffset.y);
+			}
+			else
+			{
+				this.moveSizerTo(this.sizers[0], this.state.x, this.state.y);
+			}
+		}
+	}
+
+	if (this.rotationShape != null)
+	{
+		var alpha = mxUtils.toRadians((this.currentAlpha != null) ? this.currentAlpha : this.state.style[mxConstants.STYLE_ROTATION] || '0');
+		var cos = Math.cos(alpha);
+		var sin = Math.sin(alpha);
+		
+		var ct = new mxPoint(this.state.getCenterX(), this.state.getCenterY());
+		var pt = mxUtils.getRotatedPoint(new mxPoint(s.x + s.width / 2, s.y + this.rotationHandleVSpacing), cos, sin, ct);
+
+		if (this.rotationShape.node != null)
+		{
+			this.moveSizerTo(this.rotationShape, pt.x, pt.y);
+		}
+	}
+	
+	if (this.selectionBorder != null)
+	{
+		this.selectionBorder.rotation = Number(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+	}
+	
+	if (this.edgeHandlers != null)
+	{		
+		for (var i = 0; i < this.edgeHandlers.length; i++)
+		{
+			this.edgeHandlers[i].redraw();
+		}
+	}
+
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			var temp = this.customHandles[i].shape.node.style.display;
+			this.customHandles[i].redraw();
+			this.customHandles[i].shape.node.style.display = temp;
+		}
+	}
+
+	this.updateParentHighlight();
+};
+
+/**
+ * Function: updateParentHighlight
+ * 
+ * Updates the highlight of the parent if <parentHighlightEnabled> is true.
+ */
+mxVertexHandler.prototype.updateParentHighlight = function()
+{
+	// If not destroyed
+	if (this.selectionBorder != null)
+	{
+		if (this.parentHighlight != null)
+		{
+			var parent = this.graph.model.getParent(this.state.cell);
+	
+			if (this.graph.model.isVertex(parent))
+			{
+				var pstate = this.graph.view.getState(parent);
+				var b = this.parentHighlight.bounds;
+				
+				if (pstate != null && (b.x != pstate.x || b.y != pstate.y ||
+					b.width != pstate.width || b.height != pstate.height))
+				{
+					this.parentHighlight.bounds = pstate;
+					this.parentHighlight.redraw();
+				}
+			}
+			else
+			{
+				this.parentHighlight.destroy();
+				this.parentHighlight = null;
+			}
+		}
+		else if (this.parentHighlightEnabled)
+		{
+			var parent = this.graph.model.getParent(this.state.cell);
+			
+			if (this.graph.model.isVertex(parent))
+			{
+				var pstate = this.graph.view.getState(parent);
+				
+				if (pstate != null)
+				{
+					this.parentHighlight = this.createParentHighlightShape(pstate);
+					// VML dialect required here for event transparency in IE
+					this.parentHighlight.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
+					this.parentHighlight.pointerEvents = false;
+					this.parentHighlight.rotation = Number(pstate.style[mxConstants.STYLE_ROTATION] || '0');
+					this.parentHighlight.init(this.graph.getView().getOverlayPane());
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: drawPreview
+ * 
+ * Redraws the preview.
+ */
+mxVertexHandler.prototype.drawPreview = function()
+{
+	if (this.preview != null)
+	{
+		this.preview.bounds = this.bounds;
+		
+		if (this.preview.node.parentNode == this.graph.container)
+		{
+			this.preview.bounds.width = Math.max(0, this.preview.bounds.width - 1);
+			this.preview.bounds.height = Math.max(0, this.preview.bounds.height - 1);
+		}
+	
+		this.preview.rotation = Number(this.state.style[mxConstants.STYLE_ROTATION] || '0');
+		this.preview.redraw();
+	}
+	
+	this.selectionBorder.bounds = this.bounds;
+	this.selectionBorder.redraw();
+	
+	if (this.parentHighlight != null)
+	{
+		this.parentHighlight.redraw();
+	}
+};
+
+/**
+ * Function: destroy
+ * 
+ * Destroys the handler and all its resources and DOM nodes.
+ */
+mxVertexHandler.prototype.destroy = function()
+{
+	if (this.escapeHandler != null)
+	{
+		this.state.view.graph.removeListener(this.escapeHandler);
+		this.escapeHandler = null;
+	}
+	
+	if (this.preview != null)
+	{
+		this.preview.destroy();
+		this.preview = null;
+	}
+	
+	if (this.parentHighlight != null)
+	{
+		this.parentHighlight.destroy();
+		this.parentHighlight = null;
+	}
+	
+	if (this.selectionBorder != null)
+	{
+		this.selectionBorder.destroy();
+		this.selectionBorder = null;
+	}
+	
+	this.labelShape = null;
+	this.removeHint();
+
+	if (this.sizers != null)
+	{
+		for (var i = 0; i < this.sizers.length; i++)
+		{
+			this.sizers[i].destroy();
+		}
+		
+		this.sizers = null;
+	}
+
+	if (this.customHandles != null)
+	{
+		for (var i = 0; i < this.customHandles.length; i++)
+		{
+			this.customHandles[i].destroy();
+		}
+		
+		this.customHandles = null;
+	}
+};
diff --git a/airavata-kubernetes/workflow-composer/src/js/index.txt b/airavata-kubernetes/web-console/src/assets/js/index.txt
similarity index 95%
copy from airavata-kubernetes/workflow-composer/src/js/index.txt
copy to airavata-kubernetes/web-console/src/assets/js/index.txt
index f3631d6..084fbb8 100644
--- a/airavata-kubernetes/workflow-composer/src/js/index.txt
+++ b/airavata-kubernetes/web-console/src/assets/js/index.txt
@@ -8,7 +8,7 @@ Overview:
 
   The *editor* package provides the classes required to implement a diagram
   editor. The main class in this package is <mxEditor>.
-  
+
   The *view* and *model* packages implement the graph component, represented
   by <mxGraph>. It refers to a <mxGraphModel> which contains <mxCell>s and
   caches the state of the cells in a <mxGraphView>. The cells are painted
@@ -16,15 +16,15 @@ Overview:
   Undo history is implemented in <mxUndoManager>. To display an icon on the
   graph, <mxCellOverlay> may be used. Validation rules are defined with
   <mxMultiplicity>.
-  
+
   The *handler*, *layout* and *shape* packages contain event listeners,
   layout algorithms and shapes, respectively. The graph event listeners
   include <mxRubberband> for rubberband selection, <mxTooltipHandler>
   for tooltips and <mxGraphHandler> for  basic cell modifications.
-  <mxCompactTreeLayout> implements a tree layout algorithm, and the 
+  <mxCompactTreeLayout> implements a tree layout algorithm, and the
   shape package provides various shapes, which are subclasses of
   <mxShape>.
-  
+
   The *util* package provides utility classes including <mxClipboard> for
   copy-paste, <mxDatatransfer> for drag-and-drop, <mxConstants> for keys and
   values of stylesheets, <mxEvent> and <mxUtils> for cross-browser
@@ -34,7 +34,7 @@ Overview:
   The *io* package implements a generic <mxObjectCodec> for turning
   JavaScript objects into XML. The main class is <mxCodec>.
   <mxCodecRegistry> is the global registry for custom codecs.
-  
+
 Events:
 
   There are three different types of events, namely native DOM events,
@@ -44,36 +44,36 @@ Events:
   Some helper methods for handling native events are provided in <mxEvent>. It
   also takes care of resolving cycles between DOM nodes and JavaScript event
   handlers, which can lead to memory leaks in IE6.
-  
+
   Most custom events in mxGraph are implemented using <mxEventSource>. Its
   listeners are functions that take a sender and <mxEventObject>. Additionally,
   the <mxGraph> class fires special <mxMouseEvents> which are handled using
   mouse listeners, which are objects that provide a mousedown, mousemove and
   mouseup method.
-  
+
   Events in <mxEventSource> are fired using <mxEventSource.fireEvent>.
   Listeners are added and removed using <mxEventSource.addListener> and
   <mxEventSource.removeListener>. <mxMouseEvents> in <mxGraph> are fired using
   <mxGraph.fireMouseEvent>. Listeners are added and removed using
   <mxGraph.addMouseListener> and <mxGraph.removeMouseListener>, respectively.
-  
+
 Key bindings:
-  
+
   The following key bindings are defined for mouse events in the client across
   all browsers and platforms:
-  
+
   - Control-Drag: Duplicates (clones) selected cells
   - Shift-Rightlick: Shows the context menu
   - Alt-Click: Forces rubberband (aka. marquee)
   - Control-Select: Toggles the selection state
   - Shift-Drag: Constrains the offset to one direction
   - Shift-Control-Drag: Panning (also Shift-Rightdrag)
-  
+
 Configuration:
 
   The following global variables may be defined before the client is loaded to
   specify its language or base path, respectively.
-  
+
   - mxBasePath: Specifies the path in <mxClient.basePath>.
   - mxImageBasePath: Specifies the path in <mxClient.imageBasePath>.
   - mxLanguage: Specifies the language for resources in <mxClient.language>.
@@ -86,7 +86,7 @@ Reserved Words:
   The mx prefix is used for all classes and objects in mxGraph. The mx prefix
   can be seen as the global namespace for all JavaScript code in mxGraph. The
   following fieldnames should not be used in objects.
-  
+
   - *mxObjectId*: If the object is used with mxObjectIdentity
   - *as*: If the object is a field of another object
   - *id*: If the object is an idref in a codec
@@ -100,13 +100,13 @@ Files:
 
   The library contains these relative filenames. All filenames are relative
   to <mxClient.basePath>.
-  
+
 Built-in Images:
-  
-  All images are loaded from the <mxClient.imageBasePath>, 
-  which you can change to reflect your environment. The image variables can 
+
+  All images are loaded from the <mxClient.imageBasePath>,
+  which you can change to reflect your environment. The image variables can
   also be changed individually.
-  
+
   - mxGraph.prototype.collapsedImage
   - mxGraph.prototype.expandedImage
   - mxGraph.prototype.warningImage
@@ -119,17 +119,17 @@ Built-in Images:
   - mxUtils.errorImage
   - mxConstraintHandler.prototype.pointImage
 
-  The basename of the warning image (images/warning without extension) used in 
+  The basename of the warning image (images/warning without extension) used in
   <mxGraph.setCellWarning> is defined in <mxGraph.warningImage>.
 
 Resources:
-  
+
   The <mxEditor> and <mxGraph> classes add the following resources to
   <mxResources> at class loading time:
 
   - resources/editor*.properties
   - resources/graph*.properties
-  
+
   By default, the library ships with English and German resource files.
 
 Images:
@@ -137,16 +137,16 @@ Images:
   Recommendations for using images. Use GIF images (256 color palette) in HTML
   elements (such as the toolbar and context menu), and PNG images (24 bit) for
   all images which appear inside the graph component.
-  
-  - For PNG images inside HTML elements, Internet Explorer will ignore any 
+
+  - For PNG images inside HTML elements, Internet Explorer will ignore any
     transparency information.
-  - For GIF images inside the graph, Firefox on the Mac will display strange 
-    colors. Furthermore, only the first image for animated GIFs is displayed 
+  - For GIF images inside the graph, Firefox on the Mac will display strange
+    colors. Furthermore, only the first image for animated GIFs is displayed
     on the Mac.
-    
+
   For faster image rendering during application runtime, images can be
   prefetched using the following code:
-  
+
   (code)
   var image = new Image();
   image.src = url_to_image;
@@ -164,27 +164,27 @@ Deployment:
   The deployment version of the mxClient.js file contains all required code
   in a single file. For deployment, the complete javascript/src directory is
   required.
-  
+
 Source Code:
 
-  If you are a source code customer and you wish to develop using the 
-  full source code, the commented source code is shipped in the 
-  javascript/devel/source.zip file. It contains one file for each class 
-  in mxGraph. To use the source code the source.zip file must be 
-  uncompressed and the mxClient.js URL in the HTML  page must be changed 
+  If you are a source code customer and you wish to develop using the
+  full source code, the commented source code is shipped in the
+  javascript/devel/source.zip file. It contains one file for each class
+  in mxGraph. To use the source code the source.zip file must be
+  uncompressed and the mxClient.js URL in the HTML  page must be changed
   to reference the uncompressed mxClient.js from the source.zip file.
 
 Compression:
- 
+
   When using Apache2 with mod_deflate, you can use the following directive
   in src/js/.htaccess to speedup the loading of the JavaScript sources:
-  
+
   (code)
   SetOutputFilter DEFLATE
   (end)
 
 Classes:
-  
+
   There are two types of "classes" in mxGraph: classes and singletons (where
   only one instance exists). Singletons are mapped to global objects where the
   variable name equals the classname. For example mxConstants is an object with
@@ -205,7 +205,7 @@ Subclassing:
   the superclass by assigning the prototype to an instance of the superclass,
   eg. mxEditor.prototype = new mxEventSource() and redefining the constructor
   field using mxEditor.prototype.constructor = mxEditor. The latter rule is
-  applied so that the type of an object can be retrieved via the name of it�s
+  applied so that the type of an object can be retrieved via the name of it�s
   constructor using mxUtils.getFunctionName(obj.constructor).
 
 Constructor:
@@ -222,7 +222,7 @@ Constructor:
     mxGraph.call(this, container);
   }
   (end)
-  
+
   The prototype of MyGraph inherits from mxGraph as follows. As usual, the
   constructor is redefined after extending the superclass:
 
@@ -230,7 +230,7 @@ Constructor:
   MyGraph.prototype = new mxGraph();
   MyGraph.prototype.constructor = MyGraph;
   (end)
-  
+
   You may want to define the codec associated for the class after the above
   code. This code will be executed at class loading time and makes sure the
   same codec is used to encode instances of mxGraph and MyGraph.
@@ -240,12 +240,12 @@ Constructor:
   codec.template = new MyGraph();
   mxCodecRegistry.register(codec);
   (end)
-  
+
 Functions:
 
   In the prototype for MyGraph, functions of mxGraph can then be extended as
   follows.
-  
+
   (code)
   MyGraph.prototype.isCellSelectable = function(cell)
   {
@@ -255,12 +255,12 @@ Functions:
     return selectable && (geo == null || !geo.relative);
   }
   (end)
-  
+
   The supercall in the first line is optional. It is done using the apply
   function on the isSelectable function object of the mxGraph prototype, using
   the special this and arguments variables as parameters. Calls to the
   superclass function are only possible if the function is not replaced in the
-  superclass as follows, which is another way of �subclassing� in JavaScript.
+  superclass as follows, which is another way of �subclassing� in JavaScript.
 
   (code)
   mxGraph.prototype.isCellSelectable = function(cell)
@@ -274,8 +274,8 @@ Functions:
 
   The above scheme is useful if a function definition needs to be replaced
   completely.
-  
-  In order to add new functions and fields to the subclass, the following code
+
+  In referenceId to add new functions and fields to the subclass, the following code
   is used. The example below adds a new function to return the XML
   representation of the graph model:
 
@@ -286,7 +286,7 @@ Functions:
     return enc.encode(this.getModel());
   }
   (end)
-  
+
 Variables:
 
   Likewise, a new field is declared and defined as follows.
@@ -294,7 +294,7 @@ Variables:
   (code)
   MyGraph.prototype.myField = 'Hello, World!';
   (end)
-  
+
   Note that the value assigned to myField is created only once, that is, all
   instances of MyGraph share the same value. If you require instance-specific
   values, then the field must be defined in the constructor instead.
@@ -303,7 +303,7 @@ Variables:
   function MyGraph(container)
   {
     mxGraph.call(this, container);
-    
+
     this.myField = new Array();
   }
   (end)
diff --git a/airavata-kubernetes/web-console/src/assets/js/io/mxCellCodec.js b/airavata-kubernetes/web-console/src/assets/js/io/mxCellCodec.js
new file mode 100644
index 0000000..253c96f
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/io/mxCellCodec.js
@@ -0,0 +1,189 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxCellCodec
+	 *
+	 * Codec for <mxCell>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec>
+	 * and the <mxCodecRegistry>.
+	 *
+	 * Transient Fields:
+	 *
+	 * - children
+	 * - edges
+	 * - overlays
+	 * - mxTransient
+	 *
+	 * Reference Fields:
+	 *
+	 * - parent
+	 * - source
+	 * - target
+	 * 
+	 * Transient fields can be added using the following code:
+	 * 
+	 * mxCodecRegistry.getCodec(mxCell).exclude.push('name_of_field');
+	 * 
+	 * To subclass <mxCell>, replace the template and add an alias as
+	 * follows.
+	 * 
+	 * (code)
+	 * function CustomCell(value, geometry, style)
+	 * {
+	 *   mxCell.apply(this, arguments);
+	 * }
+	 * 
+	 * mxUtils.extend(CustomCell, mxCell);
+	 * 
+	 * mxCodecRegistry.getCodec(mxCell).template = new CustomCell();
+	 * mxCodecRegistry.addAlias('CustomCell', 'mxCell');
+	 * (end)
+	 */
+	var codec = new mxObjectCodec(new mxCell(),
+		['children', 'edges', 'overlays', 'mxTransient'],
+		['parent', 'source', 'target']);
+
+	/**
+	 * Function: isCellCodec
+	 *
+	 * Returns true since this is a cell codec.
+	 */
+	codec.isCellCodec = function()
+	{
+		return true;
+	};
+
+	/**
+	 * Overidden to disable conversion of value to number.
+	 */
+	codec.isNumericAttribute = function(dec, attr, obj)
+	{
+		return attr.nodeName !== 'value' && mxObjectCodec.prototype.isNumericAttribute.apply(this, arguments);
+	};
+	
+	/**
+	 * Function: isExcluded
+	 *
+	 * Excludes user objects that are XML nodes.
+	 */ 
+	codec.isExcluded = function(obj, attr, value, isWrite)
+	{
+		return mxObjectCodec.prototype.isExcluded.apply(this, arguments) ||
+			(isWrite && attr == 'value' &&
+			value.nodeType == mxConstants.NODETYPE_ELEMENT);
+	};
+	
+	/**
+	 * Function: afterEncode
+	 *
+	 * Encodes an <mxCell> and wraps the XML up inside the
+	 * XML of the user object (inversion).
+	 */
+	codec.afterEncode = function(enc, obj, node)
+	{
+		if (obj.value != null && obj.value.nodeType == mxConstants.NODETYPE_ELEMENT)
+		{
+			// Wraps the graphical annotation up in the user object (inversion)
+			// by putting the result of the default encoding into a clone of the
+			// user object (node type 1) and returning this cloned user object.
+			var tmp = node;
+			node = mxUtils.importNode(enc.document, obj.value, true);
+			node.appendChild(tmp);
+			
+			// Moves the id attribute to the outermost XML node, namely the
+			// node which denotes the object boundaries in the file.
+			var id = tmp.getAttribute('id');
+			node.setAttribute('id', id);
+			tmp.removeAttribute('id');
+		}
+
+		return node;
+	};
+
+	/**
+	 * Function: beforeDecode
+	 *
+	 * Decodes an <mxCell> and uses the enclosing XML node as
+	 * the user object for the cell (inversion).
+	 */
+	codec.beforeDecode = function(dec, node, obj)
+	{
+		var inner = node.cloneNode(true);
+		var classname = this.getName();
+		
+		if (node.nodeName != classname)
+		{
+			// Passes the inner graphical annotation node to the
+			// object codec for further processing of the cell.
+			var tmp = node.getElementsByTagName(classname)[0];
+			
+			if (tmp != null && tmp.parentNode == node)
+			{
+				mxUtils.removeWhitespace(tmp, true);
+				mxUtils.removeWhitespace(tmp, false);
+				tmp.parentNode.removeChild(tmp);
+				inner = tmp;
+			}
+			else
+			{
+				inner = null;
+			}
+			
+			// Creates the user object out of the XML node
+			obj.value = node.cloneNode(true);
+			var id = obj.value.getAttribute('id');
+			
+			if (id != null)
+			{
+				obj.setId(id);
+				obj.value.removeAttribute('id');
+			}
+		}
+		else
+		{
+			// Uses ID from XML file as ID for cell in model
+			obj.setId(node.getAttribute('id'));
+		}
+			
+		// Preprocesses and removes all Id-references in order to use the
+		// correct encoder (this) for the known references to cells (all).
+		if (inner != null)
+		{
+			for (var i = 0; i < this.idrefs.length; i++)
+			{
+				var attr = this.idrefs[i];
+				var ref = inner.getAttribute(attr);
+				
+				if (ref != null)
+				{
+					inner.removeAttribute(attr);
+					var object = dec.objects[ref] || dec.lookup(ref);
+					
+					if (object == null)
+					{
+						// Needs to decode forward reference
+						var element = dec.getElementById(ref);
+						
+						if (element != null)
+						{
+							var decoder = mxCodecRegistry.codecs[element.nodeName] || this;
+							object = decoder.decode(dec, element);
+						}
+					}
+					
+					obj[attr] = object;
+				}
+			}
+		}
+		
+		return inner;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
diff --git a/airavata-kubernetes/web-console/src/assets/js/io/mxChildChangeCodec.js b/airavata-kubernetes/web-console/src/assets/js/io/mxChildChangeCodec.js
new file mode 100644
index 0000000..92d0c76
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/io/mxChildChangeCodec.js
@@ -0,0 +1,149 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxChildChangeCodec
+	 *
+	 * Codec for <mxChildChange>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec> and
+	 * the <mxCodecRegistry>.
+	 *
+	 * Transient Fields:
+	 *
+	 * - model
+	 * - previous
+	 * - previousIndex
+	 * - child
+	 *
+	 * Reference Fields:
+	 *
+	 * - parent
+	 */
+	var codec = new mxObjectCodec(new mxChildChange(),
+		['model', 'child', 'previousIndex'],
+		['parent', 'previous']);
+
+	/**
+	 * Function: isReference
+	 *
+	 * Returns true for the child attribute if the child
+	 * cell had a previous parent or if we're reading the
+	 * child as an attribute rather than a child node, in
+	 * which case it's always a reference.
+	 */
+	codec.isReference = function(obj, attr, value, isWrite)
+	{
+		if (attr == 'child' &&
+			(obj.previous != null ||
+			!isWrite))
+		{
+			return true;
+		}
+		
+		return mxUtils.indexOf(this.idrefs, attr) >= 0;
+	};
+
+	/**
+	 * Function: afterEncode
+	 *
+	 * Encodes the child recusively and adds the result
+	 * to the given node.
+	 */
+	codec.afterEncode = function(enc, obj, node)
+	{
+		if (this.isReference(obj, 'child',  obj.child, true))
+		{
+			// Encodes as reference (id)
+			node.setAttribute('child', enc.getId(obj.child));
+		}
+		else
+		{
+			// At this point, the encoder is no longer able to know which cells
+			// are new, so we have to encode the complete cell hierarchy and
+			// ignore the ones that are already there at decoding time. Note:
+			// This can only be resolved by moving the notify event into the
+			// execute of the edit.
+			enc.encodeCell(obj.child, node);
+		}
+		
+		return node;
+	};
+
+	/**
+	 * Function: beforeDecode
+	 *
+	 * Decodes the any child nodes as using the respective
+	 * codec from the registry.
+	 */
+	codec.beforeDecode = function(dec, node, obj)
+	{
+		if (node.firstChild != null &&
+			node.firstChild.nodeType == mxConstants.NODETYPE_ELEMENT)
+		{
+			// Makes sure the original node isn't modified
+			node = node.cloneNode(true);
+			
+			var tmp = node.firstChild;
+			obj.child = dec.decodeCell(tmp, false);
+
+			var tmp2 = tmp.nextSibling;
+			tmp.parentNode.removeChild(tmp);
+			tmp = tmp2;
+			
+			while (tmp != null)
+			{
+				tmp2 = tmp.nextSibling;
+				
+				if (tmp.nodeType == mxConstants.NODETYPE_ELEMENT)
+				{
+					// Ignores all existing cells because those do not need to
+					// be re-inserted into the model. Since the encoded version
+					// of these cells contains the new parent, this would leave
+					// to an inconsistent state on the model (ie. a parent
+					// change without a call to parentForCellChanged).
+					var id = tmp.getAttribute('id');
+					
+					if (dec.lookup(id) == null)
+					{
+						dec.decodeCell(tmp);
+					}
+				}
+				
+				tmp.parentNode.removeChild(tmp);
+				tmp = tmp2;
+			}
+		}
+		else
+		{
+			var childRef = node.getAttribute('child');
+			obj.child = dec.getObject(childRef);
+		}
+		
+		return node;
+	};
+	
+	/**
+	 * Function: afterDecode
+	 *
+	 * Restores object state in the child change.
+	 */
+	codec.afterDecode = function(dec, node, obj)
+	{
+		// Cells are encoded here after a complete transaction so the previous
+		// parent must be restored on the cell for the case where the cell was
+		// added. This is needed for the local model to identify the cell as a
+		// new cell and register the ID.
+		obj.child.parent = obj.previous;
+		obj.previous = obj.parent;
+		obj.previousIndex = obj.index;
+
+		return obj;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
diff --git a/airavata-kubernetes/web-console/src/assets/js/io/mxCodec.js b/airavata-kubernetes/web-console/src/assets/js/io/mxCodec.js
new file mode 100644
index 0000000..b308387
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/io/mxCodec.js
@@ -0,0 +1,596 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCodec
+ *
+ * XML codec for JavaScript object graphs. See <mxObjectCodec> for a
+ * description of the general encoding/decoding scheme. This class uses the
+ * codecs registered in <mxCodecRegistry> for encoding/decoding each object.
+ * 
+ * References:
+ * 
+ * In order to resolve references, especially forward references, the mxCodec
+ * constructor must be given the document that contains the referenced
+ * elements.
+ *
+ * Examples:
+ *
+ * The following code is used to encode a graph model.
+ *
+ * (code)
+ * var encoder = new mxCodec();
+ * var result = encoder.encode(graph.getModel());
+ * var xml = mxUtils.getXml(result);
+ * (end)
+ * 
+ * Example:
+ * 
+ * Using the code below, an XML document is decoded into an existing model. The
+ * document may be obtained using one of the functions in mxUtils for loading
+ * an XML file, eg. <mxUtils.get>, or using <mxUtils.parseXml> for parsing an
+ * XML string.
+ * 
+ * (code)
+ * var doc = mxUtils.parseXml(xmlString);
+ * var codec = new mxCodec(doc);
+ * codec.decode(doc.documentElement, graph.getModel());
+ * (end)
+ * 
+ * Example:
+ * 
+ * This example demonstrates parsing a list of isolated cells into an existing
+ * graph model. Note that the cells do not have a parent reference so they can
+ * be added anywhere in the cell hierarchy after parsing.
+ * 
+ * (code)
+ * var xml = '<root><mxCell id="2" value="Hello," vertex="1"><mxGeometry x="20" y="20" width="80" height="30" as="geometry"/></mxCell><mxCell id="3" value="World!" vertex="1"><mxGeometry x="200" y="150" width="80" height="30" as="geometry"/></mxCell><mxCell id="4" value="" edge="1" source="2" target="3"><mxGeometry relative="1" as="geometry"/></mxCell></root>';
+ * var doc = mxUtils.parseXml(xml);
+ * var codec = new mxCodec(doc);
+ * var elt = doc.documentElement.firstChild;
+ * var cells = [];
+ * 
+ * while (elt != null)
+ * {
+ *   cells.push(codec.decode(elt));
+ *   elt = elt.nextSibling;
+ * }
+ * 
+ * graph.addCells(cells);
+ * (end)
+ * 
+ * Example:
+ * 
+ * Using the following code, the selection cells of a graph are encoded and the
+ * output is displayed in a dialog box.
+ * 
+ * (code)
+ * var enc = new mxCodec();
+ * var cells = graph.getSelectionCells();
+ * mxUtils.alert(mxUtils.getPrettyXml(enc.encode(cells)));
+ * (end)
+ * 
+ * Newlines in the XML can be converted to <br>, in which case a '<br>' argument
+ * must be passed to <mxUtils.getXml> as the second argument.
+ * 
+ * Debugging:
+ * 
+ * For debugging I/O you can use the following code to get the sequence of
+ * encoded objects:
+ * 
+ * (code)
+ * var oldEncode = mxCodec.prototype.encode;
+ * mxCodec.prototype.encode = function(obj)
+ * {
+ *   mxLog.show();
+ *   mxLog.debug('mxCodec.encode: obj='+mxUtils.getFunctionName(obj.constructor));
+ *   
+ *   return oldEncode.apply(this, arguments);
+ * };
+ * (end)
+ * 
+ * Note that the I/O system adds object codecs for new object automatically. For
+ * decoding those objects, the constructor should be written as follows:
+ * 
+ * (code)
+ * var MyObj = function(name)
+ * {
+ *   // ...
+ * };
+ * (end)
+ * 
+ * Constructor: mxCodec
+ *
+ * Constructs an XML encoder/decoder for the specified
+ * owner document.
+ *
+ * Parameters:
+ *
+ * document - Optional XML document that contains the data.
+ * If no document is specified then a new document is created
+ * using <mxUtils.createXmlDocument>.
+ */
+function mxCodec(document)
+{
+	this.document = document || mxUtils.createXmlDocument();
+	this.objects = [];
+};
+
+/**
+ * Variable: document
+ *
+ * The owner document of the codec.
+ */
+mxCodec.prototype.document = null;
+
+/**
+ * Variable: objects
+ *
+ * Maps from IDs to objects.
+ */
+mxCodec.prototype.objects = null;
+
+/**
+ * Variable: elements
+ * 
+ * Lookup table for resolving IDs to elements.
+ */
+mxCodec.prototype.elements = null;
+
+/**
+ * Variable: encodeDefaults
+ *
+ * Specifies if default values should be encoded. Default is false.
+ */
+mxCodec.prototype.encodeDefaults = false;
+
+
+/**
+ * Function: putObject
+ * 
+ * Assoiates the given object with the given ID and returns the given object.
+ * 
+ * Parameters
+ * 
+ * id - ID for the object to be associated with.
+ * obj - Object to be associated with the ID.
+ */
+mxCodec.prototype.putObject = function(id, obj)
+{
+	this.objects[id] = obj;
+	
+	return obj;
+};
+
+/**
+ * Function: getObject
+ *
+ * Returns the decoded object for the element with the specified ID in
+ * <document>. If the object is not known then <lookup> is used to find an
+ * object. If no object is found, then the element with the respective ID
+ * from the document is parsed using <decode>.
+ */
+mxCodec.prototype.getObject = function(id)
+{
+	var obj = null;
+
+	if (id != null)
+	{
+		obj = this.objects[id];
+		
+		if (obj == null)
+		{
+			obj = this.lookup(id);
+			
+			if (obj == null)
+			{
+				var node = this.getElementById(id);
+				
+				if (node != null)
+				{
+					obj = this.decode(node);
+				}
+			}
+		}
+	}
+	
+	return obj;
+};
+
+/**
+ * Function: lookup
+ *
+ * Hook for subclassers to implement a custom lookup mechanism for cell IDs.
+ * This implementation always returns null.
+ *
+ * Example:
+ *
+ * (code)
+ * var codec = new mxCodec();
+ * codec.lookup = function(id)
+ * {
+ *   return model.getCell(id);
+ * };
+ * (end)
+ *
+ * Parameters:
+ *
+ * id - ID of the object to be returned.
+ */
+mxCodec.prototype.lookup = function(id)
+{
+	return null;
+};
+
+/**
+ * Function: getElementById
+ *
+ * Returns the element with the given ID from <document>.
+ *
+ * Parameters:
+ *
+ * id - String that contains the ID.
+ */
+mxCodec.prototype.getElementById = function(id)
+{
+	if (this.elements == null)
+	{
+		// Throws custom error for cases where a reference should be resolved
+		// in an empty document. This happens if an XML node is decoded without
+		// passing the owner document to the codec constructor.
+		if (this.document.documentElement == null)
+		{
+			throw new Error('mxCodec constructor needs document parameter');
+		}
+		
+		this.elements = new Object();
+		this.addElement(this.document.documentElement);
+	}
+	
+	return this.elements[id];
+};
+
+/**
+ * Function: addElement
+ *
+ * Adds the given element to <elements> if it has an ID.
+ */
+mxCodec.prototype.addElement = function(node)
+{
+	if (node.nodeType == mxConstants.NODETYPE_ELEMENT)
+	{
+		var id = node.getAttribute('id');
+		
+		if (id != null && this.elements[id] == null)
+		{
+			this.elements[id] = node;
+		}
+	}
+	
+	node = node.firstChild;
+	
+	while (node != null)
+	{
+		this.addElement(node);
+		node = node.nextSibling;
+	}
+};
+
+/**
+ * Function: getId
+ *
+ * Returns the ID of the specified object. This implementation
+ * calls <reference> first and if that returns null handles
+ * the object as an <mxCell> by returning their IDs using
+ * <mxCell.getId>. If no ID exists for the given cell, then
+ * an on-the-fly ID is generated using <mxCellPath.create>.
+ *
+ * Parameters:
+ *
+ * obj - Object to return the ID for.
+ */
+mxCodec.prototype.getId = function(obj)
+{
+	var id = null;
+	
+	if (obj != null)
+	{
+		id = this.reference(obj);
+		
+		if (id == null && obj instanceof mxCell)
+		{
+			id = obj.getId();
+			
+			if (id == null)
+			{
+				// Uses an on-the-fly Id
+				id = mxCellPath.create(obj);
+				
+				if (id.length == 0)
+				{
+					id = 'root';
+				}
+			}
+		}
+	}
+	
+	return id;
+};
+
+/**
+ * Function: reference
+ *
+ * Hook for subclassers to implement a custom method
+ * for retrieving IDs from objects. This implementation
+ * always returns null.
+ *
+ * Example:
+ *
+ * (code)
+ * var codec = new mxCodec();
+ * codec.reference = function(obj)
+ * {
+ *   return obj.getCustomId();
+ * };
+ * (end)
+ *
+ * Parameters:
+ *
+ * obj - Object whose ID should be returned.
+ */
+mxCodec.prototype.reference = function(obj)
+{
+	return null;
+};
+
+/**
+ * Function: encode
+ *
+ * Encodes the specified object and returns the resulting
+ * XML node.
+ *
+ * Parameters:
+ *
+ * obj - Object to be encoded. 
+ */
+mxCodec.prototype.encode = function(obj)
+{
+	var node = null;
+	
+	if (obj != null && obj.constructor != null)
+	{
+		var enc = mxCodecRegistry.getCodec(obj.constructor);
+		
+		if (enc != null)
+		{
+			node = enc.encode(this, obj);
+		}
+		else
+		{
+			if (mxUtils.isNode(obj))
+			{
+				node = mxUtils.importNode(this.document, obj, true);
+			}
+			else
+			{
+	    		mxLog.warn('mxCodec.encode: No codec for ' + mxUtils.getFunctionName(obj.constructor));
+			}
+		}
+	}
+	
+	return node;
+};
+
+/**
+ * Function: decode
+ *
+ * Decodes the given XML node. The optional "into"
+ * argument specifies an existing object to be
+ * used. If no object is given, then a new instance
+ * is created using the constructor from the codec.
+ *
+ * The function returns the passed in object or
+ * the new instance if no object was given.
+ *
+ * Parameters:
+ *
+ * node - XML node to be decoded.
+ * into - Optional object to be decodec into.
+ */
+mxCodec.prototype.decode = function(node, into)
+{
+	var obj = null;
+	
+	if (node != null && node.nodeType == mxConstants.NODETYPE_ELEMENT)
+	{
+		var ctor = null;
+		
+		try
+		{
+			ctor = window[node.nodeName];
+		}
+		catch (err)
+		{
+			// ignore
+		}
+		
+		var dec = mxCodecRegistry.getCodec(ctor);
+		
+		if (dec != null)
+		{
+			obj = dec.decode(this, node, into);
+		}
+		else
+		{
+			obj = node.cloneNode(true);
+			obj.removeAttribute('as');
+		}
+	}
+	
+	return obj;
+};
+
+/**
+ * Function: encodeCell
+ *
+ * Encoding of cell hierarchies is built-into the core, but
+ * is a higher-level function that needs to be explicitely
+ * used by the respective object encoders (eg. <mxModelCodec>,
+ * <mxChildChangeCodec> and <mxRootChangeCodec>). This
+ * implementation writes the given cell and its children as a
+ * (flat) sequence into the given node. The children are not
+ * encoded if the optional includeChildren is false. The
+ * function is in charge of adding the result into the
+ * given node and has no return value.
+ *
+ * Parameters:
+ *
+ * cell - <mxCell> to be encoded.
+ * node - Parent XML node to add the encoded cell into.
+ * includeChildren - Optional boolean indicating if the
+ * function should include all descendents. Default is true. 
+ */
+mxCodec.prototype.encodeCell = function(cell, node, includeChildren)
+{
+	node.appendChild(this.encode(cell));
+	
+	if (includeChildren == null || includeChildren)
+	{
+		var childCount = cell.getChildCount();
+		
+		for (var i = 0; i < childCount; i++)
+		{
+			this.encodeCell(cell.getChildAt(i), node);
+		}
+	}
+};
+
+/**
+ * Function: isCellCodec
+ * 
+ * Returns true if the given codec is a cell codec. This uses
+ * <mxCellCodec.isCellCodec> to check if the codec is of the
+ * given type.
+ */
+mxCodec.prototype.isCellCodec = function(codec)
+{
+	if (codec != null && typeof(codec.isCellCodec) == 'function')
+	{
+		return codec.isCellCodec();
+	}
+	
+	return false;
+};
+
+/**
+ * Function: decodeCell
+ *
+ * Decodes cells that have been encoded using inversion, ie.
+ * where the user object is the enclosing node in the XML,
+ * and restores the group and graph structure in the cells.
+ * Returns a new <mxCell> instance that represents the
+ * given node.
+ *
+ * Parameters:
+ *
+ * node - XML node that contains the cell data.
+ * restoreStructures - Optional boolean indicating whether
+ * the graph structure should be restored by calling insert
+ * and insertEdge on the parent and terminals, respectively.
+ * Default is true.
+ */
+mxCodec.prototype.decodeCell = function(node, restoreStructures)
+{
+	restoreStructures = (restoreStructures != null) ? restoreStructures : true;
+	var cell = null;
+	
+	if (node != null && node.nodeType == mxConstants.NODETYPE_ELEMENT)
+	{
+		// Tries to find a codec for the given node name. If that does
+		// not return a codec then the node is the user object (an XML node
+		// that contains the mxCell, aka inversion).
+		var decoder = mxCodecRegistry.getCodec(node.nodeName);
+		
+		// Tries to find the codec for the cell inside the user object.
+		// This assumes all node names inside the user object are either
+		// not registered or they correspond to a class for cells.
+		if (!this.isCellCodec(decoder))
+		{
+			var child = node.firstChild;
+			
+			while (child != null && !this.isCellCodec(decoder))
+			{
+				decoder = mxCodecRegistry.getCodec(child.nodeName);
+				child = child.nextSibling;
+			}
+		}
+		
+		if (!this.isCellCodec(decoder))
+		{
+			decoder = mxCodecRegistry.getCodec(mxCell);
+		}
+
+		cell = decoder.decode(this, node);
+		
+		if (restoreStructures)
+		{
+			this.insertIntoGraph(cell);
+		}
+	}
+	
+	return cell;
+};
+
+/**
+ * Function: insertIntoGraph
+ *
+ * Inserts the given cell into its parent and terminal cells.
+ */
+mxCodec.prototype.insertIntoGraph = function(cell)
+{
+	var parent = cell.parent;
+	var source = cell.getTerminal(true);
+	var target = cell.getTerminal(false);
+
+	// Fixes possible inconsistencies during insert into graph
+	cell.setTerminal(null, false);
+	cell.setTerminal(null, true);
+	cell.parent = null;
+	
+	if (parent != null)
+	{
+		parent.insert(cell);
+	}
+
+	if (source != null)
+	{
+		source.insertEdge(cell, true);
+	}
+
+	if (target != null)
+	{
+		target.insertEdge(cell, false);
+	}
+};
+
+/**
+ * Function: setAttribute
+ *
+ * Sets the attribute on the specified node to value. This is a
+ * helper method that makes sure the attribute and value arguments
+ * are not null.
+ *
+ * Parameters:
+ *
+ * node - XML node to set the attribute for.
+ * attributes - Attributename to be set.
+ * value - New value of the attribute.
+ */
+mxCodec.prototype.setAttribute = function(node, attribute, value)
+{
+	if (attribute != null && value != null)
+	{
+		node.setAttribute(attribute, value);
+	}
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/io/mxCodecRegistry.js b/airavata-kubernetes/web-console/src/assets/js/io/mxCodecRegistry.js
new file mode 100644
index 0000000..42ebcd7
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/io/mxCodecRegistry.js
@@ -0,0 +1,137 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+var mxCodecRegistry =
+{
+	/**
+	 * Class: mxCodecRegistry
+	 *
+	 * Singleton class that acts as a global registry for codecs.
+	 *
+	 * Adding an <mxCodec>:
+	 *
+	 * 1. Define a default codec with a new instance of the 
+	 * object to be handled.
+	 *
+	 * (code)
+	 * var codec = new mxObjectCodec(new mxGraphModel());
+	 * (end)
+	 *
+	 * 2. Define the functions required for encoding and decoding
+	 * objects.
+	 *
+	 * (code)
+	 * codec.encode = function(enc, obj) { ... }
+	 * codec.decode = function(dec, node, into) { ... }
+	 * (end)
+	 *
+	 * 3. Register the codec in the <mxCodecRegistry>.
+	 *
+	 * (code)
+	 * mxCodecRegistry.register(codec);
+	 * (end)
+	 *
+	 * <mxObjectCodec.decode> may be used to either create a new 
+	 * instance of an object or to configure an existing instance, 
+	 * in which case the into argument points to the existing
+	 * object. In this case, we say the codec "configures" the
+	 * object.
+	 * 
+	 * Variable: codecs
+	 *
+	 * Maps from constructor names to codecs.
+	 */
+	codecs: [],
+	
+	/**
+	 * Variable: aliases
+	 *
+	 * Maps from classnames to codecnames.
+	 */
+	aliases: [],
+
+	/**
+	 * Function: register
+	 *
+	 * Registers a new codec and associates the name of the template
+	 * constructor in the codec with the codec object.
+	 *
+	 * Parameters:
+	 *
+	 * codec - <mxObjectCodec> to be registered.
+	 */
+	register: function(codec)
+	{
+		if (codec != null)
+		{
+			var name = codec.getName();
+			mxCodecRegistry.codecs[name] = codec;
+			
+			var classname = mxUtils.getFunctionName(codec.template.constructor);
+
+			if (classname != name)
+			{
+				mxCodecRegistry.addAlias(classname, name);
+			}
+		}
+
+		return codec;
+	},
+
+	/**
+	 * Function: addAlias
+	 *
+	 * Adds an alias for mapping a classname to a codecname.
+	 */
+	addAlias: function(classname, codecname)
+	{
+		mxCodecRegistry.aliases[classname] = codecname;
+	},
+
+	/**
+	 * Function: getCodec
+	 *
+	 * Returns a codec that handles objects that are constructed
+	 * using the given constructor.
+	 *
+	 * Parameters:
+	 *
+	 * ctor - JavaScript constructor function. 
+	 */
+	getCodec: function(ctor)
+	{
+		var codec = null;
+		
+		if (ctor != null)
+		{
+			var name = mxUtils.getFunctionName(ctor);
+			var tmp = mxCodecRegistry.aliases[name];
+			
+			if (tmp != null)
+			{
+				name = tmp;
+			}
+			
+			codec = mxCodecRegistry.codecs[name];
+			
+			// Registers a new default codec for the given constructor
+			// if no codec has been previously defined.
+			if (codec == null)
+			{
+				try
+				{
+					codec = new mxObjectCodec(new ctor());
+					mxCodecRegistry.register(codec);
+				}
+				catch (e)
+				{
+					// ignore
+				}
+			}
+		}
+		
+		return codec;
+	}
+
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/io/mxDefaultKeyHandlerCodec.js b/airavata-kubernetes/web-console/src/assets/js/io/mxDefaultKeyHandlerCodec.js
new file mode 100644
index 0000000..9a18579
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/io/mxDefaultKeyHandlerCodec.js
@@ -0,0 +1,88 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxDefaultKeyHandlerCodec
+	 *
+	 * Custom codec for configuring <mxDefaultKeyHandler>s. This class is created
+	 * and registered dynamically at load time and used implicitely via
+	 * <mxCodec> and the <mxCodecRegistry>. This codec only reads configuration
+	 * data for existing key handlers, it does not encode or create key handlers.
+	 */
+	var codec = new mxObjectCodec(new mxDefaultKeyHandler());
+
+	/**
+	 * Function: encode
+	 *
+	 * Returns null.
+	 */
+	codec.encode = function(enc, obj)
+	{
+		return null;
+	};
+	
+	/**
+	 * Function: decode
+	 *
+	 * Reads a sequence of the following child nodes
+	 * and attributes:
+	 *
+	 * Child Nodes:
+	 *
+	 * add - Binds a keystroke to an actionname.
+	 *
+	 * Attributes:
+	 *
+	 * as - Keycode.
+	 * action - Actionname to execute in editor.
+	 * control - Optional boolean indicating if
+	 * 		the control key must be pressed.
+	 *
+	 * Example:
+	 *
+	 * (code)
+	 * <mxDefaultKeyHandler as="keyHandler">
+	 *   <add as="88" control="true" action="cut"/>
+	 *   <add as="67" control="true" action="copy"/>
+	 *   <add as="86" control="true" action="paste"/>
+	 * </mxDefaultKeyHandler>
+	 * (end)
+	 *
+	 * The keycodes are for the x, c and v keys.
+	 *
+	 * See also: <mxDefaultKeyHandler.bindAction>,
+	 * http://www.js-examples.com/page/tutorials__key_codes.html
+	 */
+	codec.decode = function(dec, node, into)
+	{
+		if (into != null)
+		{
+			var editor = into.editor;
+			node = node.firstChild;
+			
+			while (node != null)
+			{
+				if (!this.processInclude(dec, node, into) &&
+					node.nodeName == 'add')
+				{
+					var as = node.getAttribute('as');
+					var action = node.getAttribute('action');
+					var control = node.getAttribute('control');
+					
+					into.bindAction(as, action, control);
+				}
+				
+				node = node.nextSibling;
+			}
+		}
+		
+		return into;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
diff --git a/airavata-kubernetes/web-console/src/assets/js/io/mxDefaultPopupMenuCodec.js b/airavata-kubernetes/web-console/src/assets/js/io/mxDefaultPopupMenuCodec.js
new file mode 100644
index 0000000..7a62ac2
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/io/mxDefaultPopupMenuCodec.js
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxDefaultPopupMenuCodec
+	 *
+	 * Custom codec for configuring <mxDefaultPopupMenu>s. This class is created
+	 * and registered dynamically at load time and used implicitely via
+	 * <mxCodec> and the <mxCodecRegistry>. This codec only reads configuration
+	 * data for existing popup menus, it does not encode or create menus. Note
+	 * that this codec only passes the configuration node to the popup menu,
+	 * which uses the config to dynamically create menus. See
+	 * <mxDefaultPopupMenu.createMenu>.
+	 */
+	var codec = new mxObjectCodec(new mxDefaultPopupMenu());
+
+	/**
+	 * Function: encode
+	 *
+	 * Returns null.
+	 */
+	codec.encode = function(enc, obj)
+	{
+		return null;
+	};
+	
+	/**
+	 * Function: decode
+	 *
+	 * Uses the given node as the config for <mxDefaultPopupMenu>.
+	 */
+	codec.decode = function(dec, node, into)
+	{
+		var inc = node.getElementsByTagName('include')[0];
+		
+		if (inc != null)
+		{
+			this.processInclude(dec, inc, into);
+		}
+		else if (into != null)
+		{
+			into.config = node;
+		}
+		
+		return into;
+	};
+	
+	// Returns the codec into the registry
+	return codec;
+
+}());
diff --git a/airavata-kubernetes/web-console/src/assets/js/io/mxDefaultToolbarCodec.js b/airavata-kubernetes/web-console/src/assets/js/io/mxDefaultToolbarCodec.js
new file mode 100644
index 0000000..6157fd3
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/io/mxDefaultToolbarCodec.js
@@ -0,0 +1,312 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxDefaultToolbarCodec
+ *
+ * Custom codec for configuring <mxDefaultToolbar>s. This class is created
+ * and registered dynamically at load time and used implicitely via
+ * <mxCodec> and the <mxCodecRegistry>. This codec only reads configuration
+ * data for existing toolbars handlers, it does not encode or create toolbars.
+ */
+var mxDefaultToolbarCodec = mxCodecRegistry.register(function()
+{
+	var codec = new mxObjectCodec(new mxDefaultToolbar());
+
+	/**
+	 * Function: encode
+	 *
+	 * Returns null.
+	 */
+	codec.encode = function(enc, obj)
+	{
+		return null;
+	};
+	
+	/**
+	 * Function: decode
+	 *
+	 * Reads a sequence of the following child nodes
+	 * and attributes:
+	 *
+	 * Child Nodes:
+	 *
+	 * add - Adds a new item to the toolbar. See below for attributes.
+	 * separator - Adds a vertical separator. No attributes.
+	 * hr - Adds a horizontal separator. No attributes.
+	 * br - Adds a linefeed. No attributes. 
+	 *
+	 * Attributes:
+	 *
+	 * as - Resource key for the label.
+	 * action - Name of the action to execute in enclosing editor.
+	 * mode - Modename (see below).
+	 * template - Template name for cell insertion.
+	 * style - Optional style to override the template style.
+	 * icon - Icon (relative/absolute URL).
+	 * pressedIcon - Optional icon for pressed state (relative/absolute URL).
+	 * id - Optional ID to be used for the created DOM element.
+	 * toggle - Optional 0 or 1 to disable toggling of the element. Default is
+	 * 1 (true).
+	 *
+	 * The action, mode and template attributes are mutually exclusive. The
+	 * style can only be used with the template attribute. The add node may
+	 * contain another sequence of add nodes with as and action attributes
+	 * to create a combo box in the toolbar. If the icon is specified then
+	 * a list of the child node is expected to have its template attribute
+	 * set and the action is ignored instead.
+	 * 
+	 * Nodes with a specified template may define a function to be used for
+	 * inserting the cloned template into the graph. Here is an example of such
+	 * a node:
+	 * 
+	 * (code)
+	 * <add as="Swimlane" template="swimlane" icon="images/swimlane.gif"><![CDATA[
+	 *   function (editor, cell, evt, targetCell)
+	 *   {
+	 *     var pt = mxUtils.convertPoint(
+	 *       editor.graph.container, mxEvent.getClientX(evt),
+	 *         mxEvent.getClientY(evt));
+	 *     return editor.addVertex(targetCell, cell, pt.x, pt.y);
+	 *   }
+	 * ]]></add>
+	 * (end)
+	 * 
+	 * In the above function, editor is the enclosing <mxEditor> instance, cell
+	 * is the clone of the template, evt is the mouse event that represents the
+	 * drop and targetCell is the cell under the mousepointer where the drop
+	 * occurred. The targetCell is retrieved using <mxGraph.getCellAt>.
+	 *
+	 * Futhermore, nodes with the mode attribute may define a function to
+	 * be executed upon selection of the respective toolbar icon. In the
+	 * example below, the default edge style is set when this specific
+	 * connect-mode is activated:
+	 *
+	 * (code)
+	 * <add as="connect" mode="connect"><![CDATA[
+	 *   function (editor)
+	 *   {
+	 *     if (editor.defaultEdge != null)
+	 *     {
+	 *       editor.defaultEdge.style = 'straightEdge';
+	 *     }
+	 *   }
+	 * ]]></add>
+	 * (end)
+	 * 
+	 * Both functions require <mxDefaultToolbarCodec.allowEval> to be set to true.
+	 *
+	 * Modes:
+	 *
+	 * select - Left mouse button used for rubberband- & cell-selection.
+	 * connect - Allows connecting vertices by inserting new edges.
+	 * pan - Disables selection and switches to panning on the left button.
+	 *
+	 * Example:
+	 *
+	 * To add items to the toolbar:
+	 * 
+	 * (code)
+	 * <mxDefaultToolbar as="toolbar">
+	 *   <add as="save" action="save" icon="images/save.gif"/>
+	 *   <br/><hr/>
+	 *   <add as="select" mode="select" icon="images/select.gif"/>
+	 *   <add as="connect" mode="connect" icon="images/connect.gif"/>
+	 * </mxDefaultToolbar>
+	 * (end)
+	 */
+	codec.decode = function(dec, node, into)
+	{
+		if (into != null)
+		{
+			var editor = into.editor;
+			node = node.firstChild;
+			
+			while (node != null)
+			{
+				if (node.nodeType == mxConstants.NODETYPE_ELEMENT)
+				{
+					if (!this.processInclude(dec, node, into))
+					{
+						if (node.nodeName == 'separator')
+						{
+							into.addSeparator();
+						}
+						else if (node.nodeName == 'br')
+						{
+							into.toolbar.addBreak();
+						}
+						else if (node.nodeName == 'hr')
+						{
+							into.toolbar.addLine();
+						}
+						else if (node.nodeName == 'add')
+						{
+							var as = node.getAttribute('as');
+							as = mxResources.get(as) || as;
+							var icon = node.getAttribute('icon');
+							var pressedIcon = node.getAttribute('pressedIcon');
+							var action = node.getAttribute('action');
+							var mode = node.getAttribute('mode');
+							var template = node.getAttribute('template');
+							var toggle = node.getAttribute('toggle') != '0';
+							var text = mxUtils.getTextContent(node);
+							var elt = null;
+
+							if (action != null)
+							{
+								elt = into.addItem(as, icon, action, pressedIcon);
+							}
+							else if (mode != null)
+							{
+								var funct = (mxDefaultToolbarCodec.allowEval) ? mxUtils.eval(text) : null;
+								elt = into.addMode(as, icon, mode, pressedIcon, funct);
+							}
+							else if (template != null || (text != null && text.length > 0))
+							{
+								var cell = editor.templates[template];
+								var style = node.getAttribute('style');
+								
+								if (cell != null && style != null)
+								{
+									cell = editor.graph.cloneCells([cell])[0];
+									cell.setStyle(style);
+								}
+								
+								var insertFunction = null;
+								
+								if (text != null && text.length > 0 && mxDefaultToolbarCodec.allowEval)
+								{
+									insertFunction = mxUtils.eval(text);
+								}
+								
+								elt = into.addPrototype(as, icon, cell, pressedIcon, insertFunction, toggle);
+							}
+							else
+							{
+								var children = mxUtils.getChildNodes(node);
+								
+								if (children.length > 0)
+								{
+									if (icon == null)
+									{
+										var combo = into.addActionCombo(as);
+										
+										for (var i=0; i<children.length; i++)
+										{
+											var child = children[i];
+											
+											if (child.nodeName == 'separator')
+											{
+												into.addOption(combo, '---');
+											}
+											else if (child.nodeName == 'add')
+											{
+												var lab = child.getAttribute('as');
+												var act = child.getAttribute('action');
+												into.addActionOption(combo, lab, act);
+											}
+										}
+									}
+									else
+									{
+										var select = null;
+										var create = function()
+										{
+											var template = editor.templates[select.value];
+											
+											if (template != null)
+											{
+												var clone = template.clone();
+												var style = select.options[select.selectedIndex].cellStyle;
+												
+												if (style != null)
+												{
+													clone.setStyle(style);
+												}
+												
+												return clone;
+											}
+											else
+											{
+												mxLog.warn('Template '+template+' not found');
+											}
+											
+											return null;
+										};
+										
+										var img = into.addPrototype(as, icon, create, null, null, toggle);
+										select = into.addCombo();
+										
+										// Selects the toolbar icon if a selection change
+										// is made in the corresponding combobox.
+										mxEvent.addListener(select, 'change', function()
+										{
+											into.toolbar.selectMode(img, function(evt)
+											{
+												var pt = mxUtils.convertPoint(editor.graph.container,
+													mxEvent.getClientX(evt), mxEvent.getClientY(evt));
+												
+												return editor.addVertex(null, funct(), pt.x, pt.y);
+											});
+											
+											into.toolbar.noReset = false;
+										});
+										
+										// Adds the entries to the combobox
+										for (var i=0; i<children.length; i++)
+										{
+											var child = children[i];
+											
+											if (child.nodeName == 'separator')
+											{
+												into.addOption(select, '---');
+											}
+											else if (child.nodeName == 'add')
+											{
+												var lab = child.getAttribute('as');
+												var tmp = child.getAttribute('template');
+												var option = into.addOption(select, lab, tmp || template);
+												option.cellStyle = child.getAttribute('style');
+											}
+										}
+										
+									}
+								}
+							}
+							
+							// Assigns an ID to the created element to access it later.
+							if (elt != null)
+							{
+								var id = node.getAttribute('id');
+								
+								if (id != null && id.length > 0)
+								{
+									elt.setAttribute('id', id);
+								}
+							}
+						}
+					}
+				}
+				
+				node = node.nextSibling;
+			}
+		}
+		
+		return into;
+	};
+	
+	// Returns the codec into the registry
+	return codec;
+
+}());
+
+/**
+ * Variable: allowEval
+ * 
+ * Static global switch that specifies if the use of eval is allowed for
+ * evaluating text content. Default is true. Set this to false if stylesheets
+ * may contain user input
+ */
+mxDefaultToolbarCodec.allowEval = true;
diff --git a/airavata-kubernetes/web-console/src/assets/js/io/mxEditorCodec.js b/airavata-kubernetes/web-console/src/assets/js/io/mxEditorCodec.js
new file mode 100644
index 0000000..47ce585
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/io/mxEditorCodec.js
@@ -0,0 +1,245 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxEditorCodec
+	 *
+	 * Codec for <mxEditor>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec>
+	 * and the <mxCodecRegistry>.
+	 *
+	 * Transient Fields:
+	 *
+	 * - modified
+	 * - lastSnapshot
+	 * - ignoredChanges
+	 * - undoManager
+	 * - graphContainer
+	 * - toolbarContainer
+	 */
+	var codec = new mxObjectCodec(new mxEditor(),
+		['modified', 'lastSnapshot', 'ignoredChanges',
+		'undoManager', 'graphContainer', 'toolbarContainer']);
+
+	/**
+	 * Function: beforeDecode
+	 *
+	 * Decodes the ui-part of the configuration node by reading
+	 * a sequence of the following child nodes and attributes
+	 * and passes the control to the default decoding mechanism:
+	 *
+	 * Child Nodes:
+	 *
+	 * stylesheet - Adds a CSS stylesheet to the document.
+	 * resource - Adds the basename of a resource bundle.
+	 * add - Creates or configures a known UI element.
+	 *
+	 * These elements may appear in any order given that the
+	 * graph UI element is added before the toolbar element
+	 * (see Known Keys).
+	 *
+	 * Attributes:
+	 *
+	 * as - Key for the UI element (see below).
+	 * element - ID for the element in the document.
+	 * style - CSS style to be used for the element or window.
+	 * x - X coordinate for the new window.
+	 * y - Y coordinate for the new window.
+	 * width - Width for the new window.
+	 * height - Optional height for the new window.
+	 * name - Name of the stylesheet (absolute/relative URL).
+	 * basename - Basename of the resource bundle (see <mxResources>).
+	 *
+	 * The x, y, width and height attributes are used to create a new
+	 * <mxWindow> if the element attribute is not specified in an add
+	 * node. The name and basename are only used in the stylesheet and
+	 * resource nodes, respectively.
+	 *
+	 * Known Keys:
+	 *
+	 * graph - Main graph element (see <mxEditor.setGraphContainer>).
+	 * title - Title element (see <mxEditor.setTitleContainer>).
+	 * toolbar - Toolbar element (see <mxEditor.setToolbarContainer>).
+	 * status - Status bar element (see <mxEditor.setStatusContainer>).
+	 *
+	 * Example:
+	 *
+	 * (code)
+	 * <ui>
+	 *   <stylesheet name="css/process.css"/>
+	 *   <resource basename="resources/app"/>
+	 *   <add as="graph" element="graph"
+	 *     style="left:70px;right:20px;top:20px;bottom:40px"/>
+	 *   <add as="status" element="status"/>
+	 *   <add as="toolbar" x="10" y="20" width="54"/>
+	 * </ui>
+	 * (end)
+	 */
+	codec.afterDecode = function(dec, node, obj)
+	{
+		// Assigns the specified templates for edges
+		var defaultEdge = node.getAttribute('defaultEdge');
+		
+		if (defaultEdge != null)
+		{
+			node.removeAttribute('defaultEdge');
+			obj.defaultEdge = obj.templates[defaultEdge];
+		}
+
+		// Assigns the specified templates for groups
+		var defaultGroup = node.getAttribute('defaultGroup');
+		
+		if (defaultGroup != null)
+		{
+			node.removeAttribute('defaultGroup');
+			obj.defaultGroup = obj.templates[defaultGroup];
+		}
+
+		return obj;
+	};
+	
+	/**
+	 * Function: decodeChild
+	 * 
+	 * Overrides decode child to handle special child nodes.
+	 */	
+	codec.decodeChild = function(dec, child, obj)
+	{
+		if (child.nodeName == 'Array')
+		{
+			var role = child.getAttribute('as');
+			
+			if (role == 'templates')
+			{
+				this.decodeTemplates(dec, child, obj);
+				return;
+			}
+		}
+		else if (child.nodeName == 'ui')
+		{
+			this.decodeUi(dec, child, obj);
+			return;
+		}
+		
+		mxObjectCodec.prototype.decodeChild.apply(this, arguments);
+	};
+		
+	/**
+	 * Function: decodeTemplates
+	 *
+	 * Decodes the cells from the given node as templates.
+	 */
+	codec.decodeUi = function(dec, node, editor)
+	{
+		var tmp = node.firstChild;
+		while (tmp != null)
+		{
+			if (tmp.nodeName == 'add')
+			{
+				var as = tmp.getAttribute('as');
+				var elt = tmp.getAttribute('element');
+				var style = tmp.getAttribute('style');
+				var element = null;
+
+				if (elt != null)
+				{
+					element = document.getElementById(elt);
+					
+					if (element != null && style != null)
+					{
+						element.style.cssText += ';' + style;
+					}
+				}
+				else
+				{
+					var x = parseInt(tmp.getAttribute('x'));
+					var y = parseInt(tmp.getAttribute('y'));
+					var width = tmp.getAttribute('width');
+					var height = tmp.getAttribute('height');
+
+					// Creates a new window around the element
+					element = document.createElement('div');
+					element.style.cssText = style;
+					
+					var wnd = new mxWindow(mxResources.get(as) || as,
+						element, x, y, width, height, false, true);
+					wnd.setVisible(true);
+				}
+				
+				// TODO: Make more generic
+				if (as == 'graph')
+				{
+					editor.setGraphContainer(element);
+				}
+				else if (as == 'toolbar')
+				{
+					editor.setToolbarContainer(element);
+				}
+				else if (as == 'title')
+				{
+					editor.setTitleContainer(element);
+				}
+				else if (as == 'status')
+				{
+					editor.setStatusContainer(element);
+				}
+				else if (as == 'map')
+				{
+					editor.setMapContainer(element);
+				}
+			}
+			else if (tmp.nodeName == 'resource')
+			{
+				mxResources.add(tmp.getAttribute('basename'));
+			}
+			else if (tmp.nodeName == 'stylesheet')
+			{
+				mxClient.link('stylesheet', tmp.getAttribute('name'));
+			}
+			
+			tmp = tmp.nextSibling;
+		}	
+	};
+	
+	/**
+	 * Function: decodeTemplates
+	 *
+	 * Decodes the cells from the given node as templates.
+	 */
+	codec.decodeTemplates = function(dec, node, editor)
+	{
+		if (editor.templates == null)
+		{
+			editor.templates = [];
+		}
+		
+		var children = mxUtils.getChildNodes(node);
+		for (var j=0; j<children.length; j++)
+		{
+			var name = children[j].getAttribute('as');
+			var child = children[j].firstChild;
+			
+			while (child != null && child.nodeType != 1)
+			{
+				child = child.nextSibling;
+			}
+			
+			if (child != null)
+			{
+				// LATER: Only single cells means you need
+				// to group multiple cells within another
+				// cell. This should be changed to support
+				// arrays of cells, or the wrapper must
+				// be automatically handled in this class.
+				editor.templates[name] = dec.decodeCell(child);
+			}
+		}
+	};
+	
+	// Returns the codec into the registry
+	return codec;
+
+}());
diff --git a/airavata-kubernetes/web-console/src/assets/js/io/mxGenericChangeCodec.js b/airavata-kubernetes/web-console/src/assets/js/io/mxGenericChangeCodec.js
new file mode 100644
index 0000000..8ecc82a
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/io/mxGenericChangeCodec.js
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGenericChangeCodec
+ *
+ * Codec for <mxValueChange>s, <mxStyleChange>s, <mxGeometryChange>s,
+ * <mxCollapseChange>s and <mxVisibleChange>s. This class is created
+ * and registered dynamically at load time and used implicitely
+ * via <mxCodec> and the <mxCodecRegistry>.
+ *
+ * Transient Fields:
+ *
+ * - model
+ * - previous
+ *
+ * Reference Fields:
+ *
+ * - cell
+ * 
+ * Constructor: mxGenericChangeCodec
+ *
+ * Factory function that creates a <mxObjectCodec> for
+ * the specified change and fieldname.
+ *
+ * Parameters:
+ *
+ * obj - An instance of the change object.
+ * variable - The fieldname for the change data.
+ */
+var mxGenericChangeCodec = function(obj, variable)
+{
+	var codec = new mxObjectCodec(obj,  ['model', 'previous'], ['cell']);
+
+	/**
+	 * Function: afterDecode
+	 *
+	 * Restores the state by assigning the previous value.
+	 */
+	codec.afterDecode = function(dec, node, obj)
+	{
+		// Allows forward references in sessions. This is a workaround
+		// for the sequence of edits in mxGraph.moveCells and cellsAdded.
+		if (mxUtils.isNode(obj.cell))
+		{
+			obj.cell = dec.decodeCell(obj.cell, false);
+		}
+
+		obj.previous = obj[variable];
+
+		return obj;
+	};
+	
+	return codec;
+};
+
+// Registers the codecs
+mxCodecRegistry.register(mxGenericChangeCodec(new mxValueChange(), 'value'));
+mxCodecRegistry.register(mxGenericChangeCodec(new mxStyleChange(), 'style'));
+mxCodecRegistry.register(mxGenericChangeCodec(new mxGeometryChange(), 'geometry'));
+mxCodecRegistry.register(mxGenericChangeCodec(new mxCollapseChange(), 'collapsed'));
+mxCodecRegistry.register(mxGenericChangeCodec(new mxVisibleChange(), 'visible'));
+mxCodecRegistry.register(mxGenericChangeCodec(new mxCellAttributeChange(), 'value'));
diff --git a/airavata-kubernetes/web-console/src/assets/js/io/mxGraphCodec.js b/airavata-kubernetes/web-console/src/assets/js/io/mxGraphCodec.js
new file mode 100644
index 0000000..f3e7a56
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/io/mxGraphCodec.js
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxGraphCodec
+	 *
+	 * Codec for <mxGraph>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec>
+	 * and the <mxCodecRegistry>.
+	 *
+	 * Transient Fields:
+	 *
+	 * - graphListeners
+	 * - eventListeners
+	 * - view
+	 * - container
+	 * - cellRenderer
+	 * - editor
+	 * - selection
+	 */
+	return new mxObjectCodec(new mxGraph(),
+		['graphListeners', 'eventListeners', 'view', 'container',
+		'cellRenderer', 'editor', 'selection']);
+
+}());
diff --git a/airavata-kubernetes/web-console/src/assets/js/io/mxGraphViewCodec.js b/airavata-kubernetes/web-console/src/assets/js/io/mxGraphViewCodec.js
new file mode 100644
index 0000000..c3023de
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/io/mxGraphViewCodec.js
@@ -0,0 +1,197 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxGraphViewCodec
+	 *
+	 * Custom encoder for <mxGraphView>s. This class is created
+	 * and registered dynamically at load time and used implicitely via
+	 * <mxCodec> and the <mxCodecRegistry>. This codec only writes views
+	 * into a XML format that can be used to create an image for
+	 * the graph, that is, it contains absolute coordinates with
+	 * computed perimeters, edge styles and cell styles.
+	 */
+	var codec = new mxObjectCodec(new mxGraphView());
+
+	/**
+	 * Function: encode
+	 *
+	 * Encodes the given <mxGraphView> using <encodeCell>
+	 * starting at the model's root. This returns the
+	 * top-level graph node of the recursive encoding.
+	 */
+	codec.encode = function(enc, view)
+	{
+		return this.encodeCell(enc, view,
+			view.graph.getModel().getRoot());
+	};
+
+	/**
+	 * Function: encodeCell
+	 *
+	 * Recursively encodes the specifed cell. Uses layer
+	 * as the default nodename. If the cell's parent is
+	 * null, then graph is used for the nodename. If
+	 * <mxGraphModel.isEdge> returns true for the cell,
+	 * then edge is used for the nodename, else if
+	 * <mxGraphModel.isVertex> returns true for the cell,
+	 * then vertex is used for the nodename.
+	 *
+	 * <mxGraph.getLabel> is used to create the label
+	 * attribute for the cell. For graph nodes and vertices
+	 * the bounds are encoded into x, y, width and height.
+	 * For edges the points are encoded into a points
+	 * attribute as a space-separated list of comma-separated
+	 * coordinate pairs (eg. x0,y0 x1,y1 ... xn,yn). All
+	 * values from the cell style are added as attribute
+	 * values to the node. 
+	 */
+	codec.encodeCell = function(enc, view, cell)
+	{
+		var model = view.graph.getModel();
+		var state = view.getState(cell);
+		var parent = model.getParent(cell);
+		
+		if (parent == null || state != null)
+		{
+			var childCount = model.getChildCount(cell);
+			var geo = view.graph.getCellGeometry(cell);
+			var name = null;
+			
+			if (parent == model.getRoot())
+			{
+				name = 'layer';
+			}
+			else if (parent == null)
+			{
+				name = 'graph';
+			}
+			else if (model.isEdge(cell))
+			{
+				name = 'edge';
+			}
+			else if (childCount > 0 && geo != null)
+			{
+				name = 'group';
+			}
+			else if (model.isVertex(cell))
+			{
+				name = 'vertex';
+			}
+			
+			if (name != null)
+			{
+				var node = enc.document.createElement(name);
+				var lab = view.graph.getLabel(cell);
+				
+				if (lab != null)
+				{
+					node.setAttribute('label', view.graph.getLabel(cell));
+					
+					if (view.graph.isHtmlLabel(cell))
+					{
+						node.setAttribute('html', true);
+					}
+				}
+		
+				if (parent == null)
+				{
+					var bounds = view.getGraphBounds();
+					
+					if (bounds != null)
+					{
+						node.setAttribute('x', Math.round(bounds.x));
+						node.setAttribute('y', Math.round(bounds.y));
+						node.setAttribute('width', Math.round(bounds.width));
+						node.setAttribute('height', Math.round(bounds.height));
+					}
+					
+					node.setAttribute('scale', view.scale);
+				}
+				else if (state != null && geo != null)
+				{
+					// Writes each key, value in the style pair to an attribute
+				    for (var i in state.style)
+				    {
+				    	var value = state.style[i];
+		
+				    	// Tries to turn objects and functions into strings
+					    if (typeof(value) == 'function' &&
+							typeof(value) == 'object')
+						{
+					    	value = mxStyleRegistry.getName(value);
+				        }
+				    	
+				    	if (value != null &&
+				    		typeof(value) != 'function' &&
+							typeof(value) != 'object')
+						{
+							node.setAttribute(i, value);
+				        }
+				    }
+				    
+					var abs = state.absolutePoints;
+					
+					// Writes the list of points into one attribute
+					if (abs != null && abs.length > 0)
+					{
+						var pts = Math.round(abs[0].x) + ',' + Math.round(abs[0].y);
+		
+						for (var i=1; i<abs.length; i++)
+						{
+							pts += ' ' + Math.round(abs[i].x) + ',' +
+								Math.round(abs[i].y);
+						}
+		
+						node.setAttribute('points', pts);
+					}
+					
+					// Writes the bounds into 4 attributes
+					else
+					{
+						node.setAttribute('x', Math.round(state.x));
+						node.setAttribute('y', Math.round(state.y));
+						node.setAttribute('width', Math.round(state.width));
+						node.setAttribute('height', Math.round(state.height));				
+					}
+		
+					var offset = state.absoluteOffset;
+					
+					// Writes the offset into 2 attributes
+					if (offset != null)
+					{
+						if (offset.x != 0)
+						{
+							node.setAttribute('dx', Math.round(offset.x));
+						}
+						
+						if (offset.y != 0)
+						{
+							node.setAttribute('dy', Math.round(offset.y));
+						}
+					}
+				}
+		
+				for (var i=0; i<childCount; i++)
+				{
+					var childNode = this.encodeCell(enc,
+							view, model.getChildAt(cell, i));
+					
+					if (childNode != null)
+					{
+						node.appendChild(childNode);
+					}
+				}
+			}
+		}
+		
+		return node;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
diff --git a/airavata-kubernetes/web-console/src/assets/js/io/mxModelCodec.js b/airavata-kubernetes/web-console/src/assets/js/io/mxModelCodec.js
new file mode 100644
index 0000000..85ec4ce
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/io/mxModelCodec.js
@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxModelCodec
+	 *
+	 * Codec for <mxGraphModel>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec>
+	 * and the <mxCodecRegistry>.
+	 */
+	var codec = new mxObjectCodec(new mxGraphModel());
+
+	/**
+	 * Function: encodeObject
+	 *
+	 * Encodes the given <mxGraphModel> by writing a (flat) XML sequence of
+	 * cell nodes as produced by the <mxCellCodec>. The sequence is
+	 * wrapped-up in a node with the name root.
+	 */
+	codec.encodeObject = function(enc, obj, node)
+	{
+		var rootNode = enc.document.createElement('root');
+		enc.encodeCell(obj.getRoot(), rootNode);
+		node.appendChild(rootNode);
+	};
+
+	/**
+	 * Function: decodeChild
+	 * 
+	 * Overrides decode child to handle special child nodes.
+	 */	
+	codec.decodeChild = function(dec, child, obj)
+	{
+		if (child.nodeName == 'root')
+		{
+			this.decodeRoot(dec, child, obj);
+		}
+		else
+		{
+			mxObjectCodec.prototype.decodeChild.apply(this, arguments);
+		}
+	};
+
+	/**
+	 * Function: decodeRoot
+	 *
+	 * Reads the cells into the graph model. All cells
+	 * are children of the root element in the node.
+	 */
+	codec.decodeRoot = function(dec, root, model)
+	{
+		var rootCell = null;
+		var tmp = root.firstChild;
+		
+		while (tmp != null)
+		{
+			var cell = dec.decodeCell(tmp);
+			
+			if (cell != null && cell.getParent() == null)
+			{
+				rootCell = cell;
+			}
+			
+			tmp = tmp.nextSibling;
+		}
+
+		// Sets the root on the model if one has been decoded
+		if (rootCell != null)
+		{
+			model.setRoot(rootCell);
+		}
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
diff --git a/airavata-kubernetes/web-console/src/assets/js/io/mxObjectCodec.js b/airavata-kubernetes/web-console/src/assets/js/io/mxObjectCodec.js
new file mode 100644
index 0000000..ac69318
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/io/mxObjectCodec.js
@@ -0,0 +1,1077 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxObjectCodec
+ *
+ * Generic codec for JavaScript objects that implements a mapping between
+ * JavaScript objects and XML nodes that maps each field or element to an
+ * attribute or child node, and vice versa.
+ * 
+ * Atomic Values:
+ * 
+ * Consider the following example.
+ * 
+ * (code)
+ * var obj = new Object();
+ * obj.foo = "Foo";
+ * obj.bar = "Bar";
+ * (end)
+ * 
+ * This object is encoded into an XML node using the following.
+ * 
+ * (code)
+ * var enc = new mxCodec();
+ * var node = enc.encode(obj);
+ * (end)
+ * 
+ * The output of the encoding may be viewed using <mxLog> as follows.
+ * 
+ * (code)
+ * mxLog.show();
+ * mxLog.debug(mxUtils.getPrettyXml(node));
+ * (end)
+ * 
+ * Finally, the result of the encoding looks as follows.
+ * 
+ * (code)
+ * <Object foo="Foo" bar="Bar"/>
+ * (end)
+ * 
+ * In the above output, the foo and bar fields have been mapped to attributes
+ * with the same names, and the name of the constructor was used for the
+ * nodename.
+ * 
+ * Booleans:
+ *
+ * Since booleans are numbers in JavaScript, all boolean values are encoded
+ * into 1 for true and 0 for false. The decoder also accepts the string true
+ * and false for boolean values.
+ * 
+ * Objects:
+ * 
+ * The above scheme is applied to all atomic fields, that is, to all non-object
+ * fields of an object. For object fields, a child node is created with a
+ * special attribute that contains the fieldname. This special attribute is
+ * called "as" and hence, as is a reserved word that should not be used for a
+ * fieldname.
+ * 
+ * Consider the following example where foo is an object and bar is an atomic
+ * property of foo.
+ * 
+ * (code)
+ * var obj = {foo: {bar: "Bar"}};
+ * (end)
+ * 
+ * This will be mapped to the following XML structure by mxObjectCodec.
+ * 
+ * (code)
+ * <Object>
+ *   <Object bar="Bar" as="foo"/>
+ * </Object>
+ * (end)
+ * 
+ * In the above output, the inner Object node contains the as-attribute that
+ * specifies the fieldname in the enclosing object. That is, the field foo was
+ * mapped to a child node with an as-attribute that has the value foo.
+ * 
+ * Arrays:
+ * 
+ * Arrays are special objects that are either associative, in which case each
+ * key, value pair is treated like a field where the key is the fieldname, or
+ * they are a sequence of atomic values and objects, which is mapped to a
+ * sequence of child nodes. For object elements, the above scheme is applied
+ * without the use of the special as-attribute for creating each child. For
+ * atomic elements, a special add-node is created with the value stored in the
+ * value-attribute.
+ * 
+ * For example, the following array contains one atomic value and one object
+ * with a field called bar. Furthermore it contains two associative entries
+ * called bar with an atomic value, and foo with an object value.
+ * 
+ * (code)
+ * var obj = ["Bar", {bar: "Bar"}];
+ * obj["bar"] = "Bar";
+ * obj["foo"] = {bar: "Bar"};
+ * (end)
+ * 
+ * This array is represented by the following XML nodes.
+ * 
+ * (code)
+ * <Array bar="Bar">
+ *   <add value="Bar"/>
+ *   <Object bar="Bar"/>
+ *   <Object bar="Bar" as="foo"/>
+ * </Array>
+ * (end)
+ * 
+ * The Array node name is the name of the constructor. The additional
+ * as-attribute in the last child contains the key of the associative entry,
+ * whereas the second last child is part of the array sequence and does not
+ * have an as-attribute.
+ * 
+ * References:
+ * 
+ * Objects may be represented as child nodes or attributes with ID values,
+ * which are used to lookup the object in a table within <mxCodec>. The
+ * <isReference> function is in charge of deciding if a specific field should
+ * be encoded as a reference or not. Its default implementation returns true if
+ * the fieldname is in <idrefs>, an array of strings that is used to configure
+ * the <mxObjectCodec>.
+ * 
+ * Using this approach, the mapping does not guarantee that the referenced
+ * object itself exists in the document. The fields that are encoded as
+ * references must be carefully chosen to make sure all referenced objects
+ * exist in the document, or may be resolved by some other means if necessary.
+ * 
+ * For example, in the case of the graph model all cells are stored in a tree
+ * whose root is referenced by the model's root field. A tree is a structure
+ * that is well suited for an XML representation, however, the additional edges
+ * in the graph model have a reference to a source and target cell, which are
+ * also contained in the tree. To handle this case, the source and target cell
+ * of an edge are treated as references, whereas the children are treated as
+ * objects. Since all cells are contained in the tree and no edge references a
+ * source or target outside the tree, this setup makes sure all referenced
+ * objects are contained in the document.
+ * 
+ * In the case of a tree structure we must further avoid infinite recursion by
+ * ignoring the parent reference of each child. This is done by returning true
+ * in <isExcluded>, whose default implementation uses the array of excluded
+ * fieldnames passed to the mxObjectCodec constructor.
+ * 
+ * References are only used for cells in mxGraph. For defining other
+ * referencable object types, the codec must be able to work out the ID of an
+ * object. This is done by implementing <mxCodec.reference>. For decoding a
+ * reference, the XML node with the respective id-attribute is fetched from the
+ * document, decoded, and stored in a lookup table for later reference. For
+ * looking up external objects, <mxCodec.lookup> may be implemented.
+ * 
+ * Expressions:
+ * 
+ * For decoding JavaScript expressions, the add-node may be used with a text
+ * content that contains the JavaScript expression. For example, the following
+ * creates a field called foo in the enclosing object and assigns it the value
+ * of <mxConstants.ALIGN_LEFT>.
+ * 
+ * (code)
+ * <Object>
+ *   <add as="foo">mxConstants.ALIGN_LEFT</add>
+ * </Object>
+ * (end)
+ * 
+ * The resulting object has a field called foo with the value "left". Its XML
+ * representation looks as follows.
+ * 
+ * (code)
+ * <Object foo="left"/>
+ * (end)
+ * 
+ * This means the expression is evaluated at decoding time and the result of
+ * the evaluation is stored in the respective field. Valid expressions are all
+ * JavaScript expressions, including function definitions, which are mapped to
+ * functions on the resulting object.
+ * 
+ * Expressions are only evaluated if <allowEval> is true.
+ * 
+ * Constructor: mxObjectCodec
+ *
+ * Constructs a new codec for the specified template object.
+ * The variables in the optional exclude array are ignored by
+ * the codec. Variables in the optional idrefs array are
+ * turned into references in the XML. The optional mapping
+ * may be used to map from variable names to XML attributes.
+ * The argument is created as follows:
+ *
+ * (code)
+ * var mapping = new Object();
+ * mapping['variableName'] = 'attribute-name';
+ * (end)
+ *
+ * Parameters:
+ *
+ * template - Prototypical instance of the object to be
+ * encoded/decoded.
+ * exclude - Optional array of fieldnames to be ignored.
+ * idrefs - Optional array of fieldnames to be converted to/from
+ * references.
+ * mapping - Optional mapping from field- to attributenames.
+ */
+function mxObjectCodec(template, exclude, idrefs, mapping)
+{
+	this.template = template;
+	
+	this.exclude = (exclude != null) ? exclude : [];
+	this.idrefs = (idrefs != null) ? idrefs : [];
+	this.mapping = (mapping != null) ? mapping : [];
+	
+	this.reverse = new Object();
+	
+	for (var i in this.mapping)
+	{
+		this.reverse[this.mapping[i]] = i;
+	}
+};
+
+/**
+ * Variable: allowEval
+ *
+ * Static global switch that specifies if expressions in arrays are allowed.
+ * Default is false. NOTE: Enabling this carries a possible security risk.
+ */
+mxObjectCodec.allowEval = false;
+
+/**
+ * Variable: template
+ *
+ * Holds the template object associated with this codec.
+ */
+mxObjectCodec.prototype.template = null;
+
+/**
+ * Variable: exclude
+ *
+ * Array containing the variable names that should be
+ * ignored by the codec.
+ */
+mxObjectCodec.prototype.exclude = null;
+
+/**
+ * Variable: idrefs
+ *
+ * Array containing the variable names that should be
+ * turned into or converted from references. See
+ * <mxCodec.getId> and <mxCodec.getObject>.
+ */
+mxObjectCodec.prototype.idrefs = null;
+
+/**
+ * Variable: mapping
+ *
+ * Maps from from fieldnames to XML attribute names.
+ */
+mxObjectCodec.prototype.mapping = null;
+
+/**
+ * Variable: reverse
+ *
+ * Maps from from XML attribute names to fieldnames.
+ */
+mxObjectCodec.prototype.reverse = null;
+
+/**
+ * Function: getName
+ * 
+ * Returns the name used for the nodenames and lookup of the codec when
+ * classes are encoded and nodes are decoded. For classes to work with
+ * this the codec registry automatically adds an alias for the classname
+ * if that is different than what this returns. The default implementation
+ * returns the classname of the template class.
+ */
+mxObjectCodec.prototype.getName = function()
+{
+	return mxUtils.getFunctionName(this.template.constructor);
+};
+
+/**
+ * Function: cloneTemplate
+ * 
+ * Returns a new instance of the template for this codec.
+ */
+mxObjectCodec.prototype.cloneTemplate = function()
+{
+	return new this.template.constructor();
+};
+
+/**
+ * Function: getFieldName
+ * 
+ * Returns the fieldname for the given attributename.
+ * Looks up the value in the <reverse> mapping or returns
+ * the input if there is no reverse mapping for the
+ * given name.
+ */
+mxObjectCodec.prototype.getFieldName = function(attributename)
+{
+	if (attributename != null)
+	{
+		var mapped = this.reverse[attributename];
+		
+		if (mapped != null)
+		{
+			attributename = mapped;
+		}
+	}
+	
+	return attributename;
+};
+
+/**
+ * Function: getAttributeName
+ * 
+ * Returns the attributename for the given fieldname.
+ * Looks up the value in the <mapping> or returns
+ * the input if there is no mapping for the
+ * given name.
+ */
+mxObjectCodec.prototype.getAttributeName = function(fieldname)
+{
+	if (fieldname != null)
+	{
+		var mapped = this.mapping[fieldname];
+		
+		if (mapped != null)
+		{
+			fieldname = mapped;
+		}
+	}
+	
+	return fieldname;
+};
+
+/**
+ * Function: isExcluded
+ *
+ * Returns true if the given attribute is to be ignored by the codec. This
+ * implementation returns true if the given fieldname is in <exclude> or
+ * if the fieldname equals <mxObjectIdentity.FIELD_NAME>.
+ *
+ * Parameters:
+ *
+ * obj - Object instance that contains the field.
+ * attr - Fieldname of the field.
+ * value - Value of the field.
+ * write - Boolean indicating if the field is being encoded or decoded.
+ * Write is true if the field is being encoded, else it is being decoded.
+ */
+mxObjectCodec.prototype.isExcluded = function(obj, attr, value, write)
+{
+	return attr == mxObjectIdentity.FIELD_NAME ||
+		mxUtils.indexOf(this.exclude, attr) >= 0;
+};
+
+/**
+ * Function: isReference
+ *
+ * Returns true if the given fieldname is to be treated
+ * as a textual reference (ID). This implementation returns
+ * true if the given fieldname is in <idrefs>.
+ *
+ * Parameters:
+ *
+ * obj - Object instance that contains the field.
+ * attr - Fieldname of the field.
+ * value - Value of the field. 
+ * write - Boolean indicating if the field is being encoded or decoded.
+ * Write is true if the field is being encoded, else it is being decoded.
+ */
+mxObjectCodec.prototype.isReference = function(obj, attr, value, write)
+{
+	return mxUtils.indexOf(this.idrefs, attr) >= 0;
+};
+
+/**
+ * Function: encode
+ *
+ * Encodes the specified object and returns a node
+ * representing then given object. Calls <beforeEncode>
+ * after creating the node and <afterEncode> with the 
+ * resulting node after processing.
+ *
+ * Enc is a reference to the calling encoder. It is used
+ * to encode complex objects and create references.
+ *
+ * This implementation encodes all variables of an
+ * object according to the following rules:
+ *
+ * - If the variable name is in <exclude> then it is ignored.
+ * - If the variable name is in <idrefs> then <mxCodec.getId>
+ * is used to replace the object with its ID.
+ * - The variable name is mapped using <mapping>.
+ * - If obj is an array and the variable name is numeric
+ * (ie. an index) then it is not encoded.
+ * - If the value is an object, then the codec is used to
+ * create a child node with the variable name encoded into
+ * the "as" attribute.
+ * - Else, if <encodeDefaults> is true or the value differs
+ * from the template value, then ...
+ * - ... if obj is not an array, then the value is mapped to
+ * an attribute.
+ * - ... else if obj is an array, the value is mapped to an
+ * add child with a value attribute or a text child node,
+ * if the value is a function.
+ *
+ * If no ID exists for a variable in <idrefs> or if an object
+ * cannot be encoded, a warning is issued using <mxLog.warn>.
+ *
+ * Returns the resulting XML node that represents the given
+ * object.
+ *
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Object to be encoded.
+ */
+mxObjectCodec.prototype.encode = function(enc, obj)
+{
+	var node = enc.document.createElement(this.getName());
+	
+	obj = this.beforeEncode(enc, obj, node);
+	this.encodeObject(enc, obj, node);
+	
+	return this.afterEncode(enc, obj, node);
+};
+	
+/**
+ * Function: encodeObject
+ *
+ * Encodes the value of each member in then given obj into the given node using
+ * <encodeValue>.
+ * 
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Object to be encoded.
+ * node - XML node that contains the encoded object.
+ */
+mxObjectCodec.prototype.encodeObject = function(enc, obj, node)
+{
+	enc.setAttribute(node, 'id', enc.getId(obj));
+	
+    for (var i in obj)
+    {
+		var name = i;
+		var value = obj[name];
+		
+    	if (value != null && !this.isExcluded(obj, name, value, true))
+    	{
+    		if (mxUtils.isInteger(name))
+    		{
+    			name = null;
+    		}
+    		
+    		this.encodeValue(enc, obj, name, value, node);
+    	}
+    }
+};
+
+/**
+ * Function: encodeValue
+ * 
+ * Converts the given value according to the mappings
+ * and id-refs in this codec and uses <writeAttribute>
+ * to write the attribute into the given node.
+ * 
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Object whose property is going to be encoded.
+ * name - XML node that contains the encoded object.
+ * value - Value of the property to be encoded.
+ * node - XML node that contains the encoded object.
+ */
+mxObjectCodec.prototype.encodeValue = function(enc, obj, name, value, node)
+{
+	if (value != null)
+	{
+		if (this.isReference(obj, name, value, true))
+		{
+			var tmp = enc.getId(value);
+			
+			if (tmp == null)
+			{
+		    	mxLog.warn('mxObjectCodec.encode: No ID for ' +
+		    		this.getName() + '.' + name + '=' + value);
+		    	return; // exit
+		    }
+		    
+		    value = tmp;
+		}
+
+		var defaultValue = this.template[name];
+		
+		// Checks if the value is a default value and
+		// the name is correct
+		if (name == null || enc.encodeDefaults || defaultValue != value)
+		{
+			name = this.getAttributeName(name);
+			this.writeAttribute(enc, obj, name, value, node);	
+		}
+	}
+};
+
+/**
+ * Function: writeAttribute
+ * 
+ * Writes the given value into node using <writePrimitiveAttribute>
+ * or <writeComplexAttribute> depending on the type of the value.
+ */
+mxObjectCodec.prototype.writeAttribute = function(enc, obj, name, value, node)
+{
+	if (typeof(value) != 'object' /* primitive type */)
+	{
+		this.writePrimitiveAttribute(enc, obj, name, value, node);
+	}
+	else /* complex type */
+	{
+		this.writeComplexAttribute(enc, obj, name, value, node);
+	}
+};
+
+/**
+ * Function: writePrimitiveAttribute
+ * 
+ * Writes the given value as an attribute of the given node.
+ */
+mxObjectCodec.prototype.writePrimitiveAttribute = function(enc, obj, name, value, node)
+{
+	value = this.convertAttributeToXml(enc, obj, name, value, node);
+	
+	if (name == null)
+	{
+		var child = enc.document.createElement('add');
+		
+		if (typeof(value) == 'function')
+		{
+    		child.appendChild(enc.document.createTextNode(value));
+    	}
+    	else
+    	{
+    		enc.setAttribute(child, 'value', value);
+    	}
+    	
+		node.appendChild(child);
+	}
+	else if (typeof(value) != 'function')
+	{
+    	enc.setAttribute(node, name, value);
+	}		
+};
+	
+/**
+ * Function: writeComplexAttribute
+ * 
+ * Writes the given value as a child node of the given node.
+ */
+mxObjectCodec.prototype.writeComplexAttribute = function(enc, obj, name, value, node)
+{
+	var child = enc.encode(value);
+	
+	if (child != null)
+	{
+		if (name != null)
+		{
+    		child.setAttribute('as', name);
+    	}
+    	
+    	node.appendChild(child);
+	}
+	else
+	{
+		mxLog.warn('mxObjectCodec.encode: No node for ' + this.getName() + '.' + name + ': ' + value);
+	}
+};
+
+/**
+ * Function: convertAttributeToXml
+ * 
+ * Converts true to "1" and false to "0" is <isBooleanAttribute> returns true.
+ * All other values are not converted.
+ * 
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Objec to convert the attribute for.
+ * name - Name of the attribute to be converted.
+ * value - Value to be converted.
+ */
+mxObjectCodec.prototype.convertAttributeToXml = function(enc, obj, name, value)
+{
+	// Makes sure to encode boolean values as numeric values
+	if (this.isBooleanAttribute(enc, obj, name, value))
+	{	
+		// Checks if the value is true (do not use the value as is, because
+		// this would check if the value is not null, so 0 would be true)
+		value = (value == true) ? '1' : '0';
+	}
+	
+	return value;
+};
+
+/**
+ * Function: isBooleanAttribute
+ * 
+ * Returns true if the given object attribute is a boolean value.
+ * 
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Objec to convert the attribute for.
+ * name - Name of the attribute to be converted.
+ * value - Value of the attribute to be converted.
+ */
+mxObjectCodec.prototype.isBooleanAttribute = function(enc, obj, name, value)
+{
+	return (typeof(value.length) == 'undefined' && (value == true || value == false));
+};
+
+/**
+ * Function: convertAttributeFromXml
+ * 
+ * Converts booleans and numeric values to the respective types. Values are
+ * numeric if <isNumericAttribute> returns true.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * attr - XML attribute to be converted.
+ * obj - Objec to convert the attribute for.
+ */
+mxObjectCodec.prototype.convertAttributeFromXml = function(dec, attr, obj)
+{
+	var value = attr.value;
+	
+	if (this.isNumericAttribute(dec, attr, obj))
+	{
+		value = parseFloat(value);
+	}
+	
+	return value;
+};
+
+/**
+ * Function: isNumericAttribute
+ * 
+ * Returns true if the given XML attribute is a numeric value.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * attr - XML attribute to be converted.
+ * obj - Objec to convert the attribute for.
+ */
+mxObjectCodec.prototype.isNumericAttribute = function(dec, attr, obj)
+{
+	return mxUtils.isNumeric(attr.value);
+};
+
+/**
+ * Function: beforeEncode
+ *
+ * Hook for subclassers to pre-process the object before
+ * encoding. This returns the input object. The return
+ * value of this function is used in <encode> to perform
+ * the default encoding into the given node.
+ *
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Object to be encoded.
+ * node - XML node to encode the object into.
+ */
+mxObjectCodec.prototype.beforeEncode = function(enc, obj, node)
+{
+	return obj;
+};
+
+/**
+ * Function: afterEncode
+ *
+ * Hook for subclassers to post-process the node
+ * for the given object after encoding and return the
+ * post-processed node. This implementation returns 
+ * the input node. The return value of this method
+ * is returned to the encoder from <encode>.
+ *
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * obj - Object to be encoded.
+ * node - XML node that represents the default encoding.
+ */
+mxObjectCodec.prototype.afterEncode = function(enc, obj, node)
+{
+	return node;
+};
+
+/**
+ * Function: decode
+ *
+ * Parses the given node into the object or returns a new object
+ * representing the given node.
+ *
+ * Dec is a reference to the calling decoder. It is used to decode
+ * complex objects and resolve references.
+ *
+ * If a node has an id attribute then the object cache is checked for the
+ * object. If the object is not yet in the cache then it is constructed
+ * using the constructor of <template> and cached in <mxCodec.objects>.
+ *
+ * This implementation decodes all attributes and childs of a node
+ * according to the following rules:
+ *
+ * - If the variable name is in <exclude> or if the attribute name is "id"
+ * or "as" then it is ignored.
+ * - If the variable name is in <idrefs> then <mxCodec.getObject> is used
+ * to replace the reference with an object.
+ * - The variable name is mapped using a reverse <mapping>.
+ * - If the value has a child node, then the codec is used to create a
+ * child object with the variable name taken from the "as" attribute.
+ * - If the object is an array and the variable name is empty then the
+ * value or child object is appended to the array.
+ * - If an add child has no value or the object is not an array then
+ * the child text content is evaluated using <mxUtils.eval>.
+ *
+ * For add nodes where the object is not an array and the variable name
+ * is defined, the default mechanism is used, allowing to override/add
+ * methods as follows:
+ *
+ * (code)
+ * <Object>
+ *   <add as="hello"><![CDATA[
+ *     function(arg1) {
+ *       mxUtils.alert('Hello '+arg1);
+ *     }
+ *   ]]></add>
+ * </Object>
+ * (end) 
+ *
+ * If no object exists for an ID in <idrefs> a warning is issued
+ * using <mxLog.warn>.
+ *
+ * Returns the resulting object that represents the given XML node
+ * or the object given to the method as the into parameter.
+ *
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * node - XML node to be decoded.
+ * into - Optional objec to encode the node into.
+ */
+mxObjectCodec.prototype.decode = function(dec, node, into)
+{
+	var id = node.getAttribute('id');
+	var obj = dec.objects[id];
+	
+	if (obj == null)
+	{
+		obj = into || this.cloneTemplate();
+		
+		if (id != null)
+		{
+			dec.putObject(id, obj);
+		}
+	}
+	
+	node = this.beforeDecode(dec, node, obj);
+	this.decodeNode(dec, node, obj);
+	
+    return this.afterDecode(dec, node, obj);
+};	
+
+/**
+ * Function: decodeNode
+ * 
+ * Calls <decodeAttributes> and <decodeChildren> for the given node.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * node - XML node to be decoded.
+ * obj - Objec to encode the node into.
+ */	
+mxObjectCodec.prototype.decodeNode = function(dec, node, obj)
+{
+	if (node != null)
+	{
+		this.decodeAttributes(dec, node, obj);
+		this.decodeChildren(dec, node, obj);
+	}
+};
+
+/**
+ * Function: decodeAttributes
+ * 
+ * Decodes all attributes of the given node using <decodeAttribute>.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * node - XML node to be decoded.
+ * obj - Objec to encode the node into.
+ */	
+mxObjectCodec.prototype.decodeAttributes = function(dec, node, obj)
+{
+	var attrs = node.attributes;
+	
+	if (attrs != null)
+	{
+		for (var i = 0; i < attrs.length; i++)
+		{
+			this.decodeAttribute(dec, attrs[i], obj);
+		}
+	}
+};
+
+/**
+ * Function: isIgnoredAttribute
+ * 
+ * Returns true if the given attribute should be ignored. This implementation
+ * returns true if the attribute name is "as" or "id".
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * attr - XML attribute to be decoded.
+ * obj - Objec to encode the attribute into.
+ */	
+mxObjectCodec.prototype.isIgnoredAttribute = function(dec, attr, obj)
+{
+	return attr.nodeName == 'as' || attr.nodeName == 'id';
+};
+
+/**
+ * Function: decodeAttribute
+ * 
+ * Reads the given attribute into the specified object.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * attr - XML attribute to be decoded.
+ * obj - Objec to encode the attribute into.
+ */	
+mxObjectCodec.prototype.decodeAttribute = function(dec, attr, obj)
+{
+	if (!this.isIgnoredAttribute(dec, attr, obj))
+	{
+		var name = attr.nodeName;
+		
+		// Converts the string true and false to their boolean values.
+		// This may require an additional check on the obj to see if
+		// the existing field is a boolean value or uninitialized, in
+		// which case we may want to convert true and false to a string.
+		var value = this.convertAttributeFromXml(dec, attr, obj);
+		var fieldname = this.getFieldName(name);
+		
+		if (this.isReference(obj, fieldname, value, false))
+		{
+			var tmp = dec.getObject(value);
+			
+			if (tmp == null)
+			{
+		    	mxLog.warn('mxObjectCodec.decode: No object for ' +
+		    		this.getName() + '.' + name + '=' + value);
+		    	return; // exit
+		    }
+		    
+		    value = tmp;
+		}
+
+		if (!this.isExcluded(obj, name, value, false))
+		{
+			//mxLog.debug(mxUtils.getFunctionName(obj.constructor)+'.'+name+'='+value);
+			obj[name] = value;
+		}
+	}
+};
+
+/**
+ * Function: decodeChildren
+ * 
+ * Decodes all children of the given node using <decodeChild>.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * node - XML node to be decoded.
+ * obj - Objec to encode the node into.
+ */	
+mxObjectCodec.prototype.decodeChildren = function(dec, node, obj)
+{
+	var child = node.firstChild;
+	
+	while (child != null)
+	{
+		var tmp = child.nextSibling;
+		
+		if (child.nodeType == mxConstants.NODETYPE_ELEMENT &&
+			!this.processInclude(dec, child, obj))
+		{
+			this.decodeChild(dec, child, obj);
+		}
+		
+		child = tmp;
+	}
+};
+
+/**
+ * Function: decodeChild
+ * 
+ * Reads the specified child into the given object.
+ * 
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * child - XML child element to be decoded.
+ * obj - Objec to encode the node into.
+ */	
+mxObjectCodec.prototype.decodeChild = function(dec, child, obj)
+{
+	var fieldname = this.getFieldName(child.getAttribute('as'));
+	
+	if (fieldname == null || !this.isExcluded(obj, fieldname, child, false))
+	{
+		var template = this.getFieldTemplate(obj, fieldname, child);
+		var value = null;
+		
+		if (child.nodeName == 'add')
+		{
+			value = child.getAttribute('value');
+			
+			if (value == null && mxObjectCodec.allowEval)
+			{
+				value = mxUtils.eval(mxUtils.getTextContent(child));
+			}
+		}
+		else
+		{
+			value = dec.decode(child, template);
+		}
+
+		this.addObjectValue(obj, fieldname, value, template);
+	}
+};
+
+/**
+ * Function: getFieldTemplate
+ * 
+ * Returns the template instance for the given field. This returns the
+ * value of the field, null if the value is an array or an empty collection
+ * if the value is a collection. The value is then used to populate the
+ * field for a new instance. For strongly typed languages it may be
+ * required to override this to return the correct collection instance
+ * based on the encoded child.
+ */	
+mxObjectCodec.prototype.getFieldTemplate = function(obj, fieldname, child)
+{
+	var template = obj[fieldname];
+	
+	// Non-empty arrays are replaced completely
+    if (template instanceof Array && template.length > 0)
+    {
+        template = null;
+    }
+    
+    return template;
+};
+
+/**
+ * Function: addObjectValue
+ * 
+ * Sets the decoded child node as a value of the given object. If the
+ * object is a map, then the value is added with the given fieldname as a
+ * key. If the fieldname is not empty, then setFieldValue is called or
+ * else, if the object is a collection, the value is added to the
+ * collection. For strongly typed languages it may be required to
+ * override this with the correct code to add an entry to an object.
+ */	
+mxObjectCodec.prototype.addObjectValue = function(obj, fieldname, value, template)
+{
+	if (value != null && value != template)
+	{
+		if (fieldname != null && fieldname.length > 0)
+		{
+			obj[fieldname] = value;
+		}
+		else
+		{
+			obj.push(value);
+		}
+		//mxLog.debug('Decoded '+mxUtils.getFunctionName(obj.constructor)+'.'+fieldname+': '+value);
+	}
+};
+
+/**
+ * Function: processInclude
+ *
+ * Returns true if the given node is an include directive and
+ * executes the include by decoding the XML document. Returns
+ * false if the given node is not an include directive.
+ *
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the encoding/decoding process.
+ * node - XML node to be checked.
+ * into - Optional object to pass-thru to the codec.
+ */
+mxObjectCodec.prototype.processInclude = function(dec, node, into)
+{
+	if (node.nodeName == 'include')
+	{
+		var name = node.getAttribute('name');
+		
+		if (name != null)
+		{
+			try
+			{
+				var xml = mxUtils.load(name).getDocumentElement();
+				
+				if (xml != null)
+				{
+					dec.decode(xml, into);
+				}
+			}
+			catch (e)
+			{
+				// ignore
+			}
+		}
+		
+		return true;
+	}
+	
+	return false;
+};
+
+/**
+ * Function: beforeDecode
+ *
+ * Hook for subclassers to pre-process the node for
+ * the specified object and return the node to be
+ * used for further processing by <decode>.
+ * The object is created based on the template in the 
+ * calling method and is never null. This implementation
+ * returns the input node. The return value of this
+ * function is used in <decode> to perform
+ * the default decoding into the given object.
+ *
+ * Parameters:
+ *
+ * dec - <mxCodec> that controls the decoding process.
+ * node - XML node to be decoded.
+ * obj - Object to encode the node into.
+ */
+mxObjectCodec.prototype.beforeDecode = function(dec, node, obj)
+{
+	return node;
+};
+
+/**
+ * Function: afterDecode
+ *
+ * Hook for subclassers to post-process the object after
+ * decoding. This implementation returns the given object
+ * without any changes. The return value of this method
+ * is returned to the decoder from <decode>.
+ *
+ * Parameters:
+ *
+ * enc - <mxCodec> that controls the encoding process.
+ * node - XML node to be decoded.
+ * obj - Object that represents the default decoding.
+ */
+mxObjectCodec.prototype.afterDecode = function(dec, node, obj)
+{
+	return obj;
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/io/mxRootChangeCodec.js b/airavata-kubernetes/web-console/src/assets/js/io/mxRootChangeCodec.js
new file mode 100644
index 0000000..bf8c47d
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/io/mxRootChangeCodec.js
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxRootChangeCodec
+	 *
+	 * Codec for <mxRootChange>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec> and
+	 * the <mxCodecRegistry>.
+	 *
+	 * Transient Fields:
+	 *
+	 * - model
+	 * - previous
+	 * - root
+	 */
+	var codec = new mxObjectCodec(new mxRootChange(),
+		['model', 'previous', 'root']);
+
+	/**
+	 * Function: onEncode
+	 *
+	 * Encodes the child recursively.
+	 */
+	codec.afterEncode = function(enc, obj, node)
+	{
+		enc.encodeCell(obj.root, node);
+		
+		return node;
+	};
+
+	/**
+	 * Function: beforeDecode
+	 *
+	 * Decodes the optional children as cells
+	 * using the respective decoder.
+	 */
+	codec.beforeDecode = function(dec, node, obj)
+	{
+		if (node.firstChild != null &&
+			node.firstChild.nodeType == mxConstants.NODETYPE_ELEMENT)
+		{
+			// Makes sure the original node isn't modified
+			node = node.cloneNode(true);
+			
+			var tmp = node.firstChild;
+			obj.root = dec.decodeCell(tmp, false);
+
+			var tmp2 = tmp.nextSibling;
+			tmp.parentNode.removeChild(tmp);
+			tmp = tmp2;
+		
+			while (tmp != null)
+			{
+				tmp2 = tmp.nextSibling;
+				dec.decodeCell(tmp);
+				tmp.parentNode.removeChild(tmp);
+				tmp = tmp2;
+			}
+		}
+		
+		return node;
+	};
+	
+	/**
+	 * Function: afterDecode
+	 *
+	 * Restores the state by assigning the previous value.
+	 */
+	codec.afterDecode = function(dec, node, obj)
+	{
+		obj.previous = obj.root;
+		
+		return obj;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
diff --git a/airavata-kubernetes/web-console/src/assets/js/io/mxStylesheetCodec.js b/airavata-kubernetes/web-console/src/assets/js/io/mxStylesheetCodec.js
new file mode 100644
index 0000000..8899116
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/io/mxStylesheetCodec.js
@@ -0,0 +1,217 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxStylesheetCodec
+ *
+ * Codec for <mxStylesheet>s. This class is created and registered
+ * dynamically at load time and used implicitely via <mxCodec>
+ * and the <mxCodecRegistry>.
+ */
+var mxStylesheetCodec = mxCodecRegistry.register(function()
+{
+	var codec = new mxObjectCodec(new mxStylesheet());
+
+	/**
+	 * Function: encode
+	 *
+	 * Encodes a stylesheet. See <decode> for a description of the
+	 * format.
+	 */
+	codec.encode = function(enc, obj)
+	{
+		var node = enc.document.createElement(this.getName());
+		
+		for (var i in obj.styles)
+		{
+			var style = obj.styles[i];
+			var styleNode = enc.document.createElement('add');
+			
+			if (i != null)
+			{
+				styleNode.setAttribute('as', i);
+				
+				for (var j in style)
+				{
+					var value = this.getStringValue(j, style[j]);
+					
+					if (value != null)
+					{
+						var entry = enc.document.createElement('add');
+						entry.setAttribute('value', value);
+						entry.setAttribute('as', j);
+						styleNode.appendChild(entry);
+					}
+				}
+				
+				if (styleNode.childNodes.length > 0)
+				{
+					node.appendChild(styleNode);
+				}
+			}
+		}
+		
+	    return node;
+	};
+
+	/**
+	 * Function: getStringValue
+	 *
+	 * Returns the string for encoding the given value.
+	 */
+	codec.getStringValue = function(key, value)
+	{
+		var type = typeof(value);
+		
+		if (type == 'function')
+		{
+			value = mxStyleRegistry.getName(style[j]);
+		}
+		else if (type == 'object')
+		{
+			value = null;
+		}
+		
+		return value;
+	};
+	
+	/**
+	 * Function: decode
+	 *
+	 * Reads a sequence of the following child nodes
+	 * and attributes:
+	 *
+	 * Child Nodes:
+	 *
+	 * add - Adds a new style.
+	 *
+	 * Attributes:
+	 *
+	 * as - Name of the style.
+	 * extend - Name of the style to inherit from.
+	 *
+	 * Each node contains another sequence of add and remove nodes with the following
+	 * attributes:
+	 *
+	 * as - Name of the style (see <mxConstants>).
+	 * value - Value for the style.
+	 *
+	 * Instead of the value-attribute, one can put Javascript expressions into
+	 * the node as follows if <mxStylesheetCodec.allowEval> is true:
+	 * <add as="perimeter">mxPerimeter.RectanglePerimeter</add>
+	 *
+	 * A remove node will remove the entry with the name given in the as-attribute
+	 * from the style.
+	 * 
+	 * Example:
+	 *
+	 * (code)
+	 * <mxStylesheet as="stylesheet">
+	 *   <add as="text">
+	 *     <add as="fontSize" value="12"/>
+	 *   </add>
+	 *   <add as="defaultVertex" extend="text">
+	 *     <add as="shape" value="rectangle"/>
+	 *   </add>
+	 * </mxStylesheet>
+	 * (end)
+	 */
+	codec.decode = function(dec, node, into)
+	{
+		var obj = into || new this.template.constructor();
+		var id = node.getAttribute('id');
+		
+		if (id != null)
+		{
+			dec.objects[id] = obj;
+		}
+		
+		node = node.firstChild;
+		
+		while (node != null)
+		{
+			if (!this.processInclude(dec, node, obj) && node.nodeName == 'add')
+			{
+				var as = node.getAttribute('as');
+				
+				if (as != null)
+				{
+					var extend = node.getAttribute('extend');
+					var style = (extend != null) ? mxUtils.clone(obj.styles[extend]) : null;
+					
+					if (style == null)
+					{
+						if (extend != null)
+						{
+							mxLog.warn('mxStylesheetCodec.decode: stylesheet ' +
+								extend + ' not found to extend');
+						}
+						
+						style = new Object();
+					}
+					
+					var entry = node.firstChild;
+					
+					while (entry != null)
+					{
+						if (entry.nodeType == mxConstants.NODETYPE_ELEMENT)
+						{
+						 	var key = entry.getAttribute('as');
+						 	
+						 	if (entry.nodeName == 'add')
+						 	{
+							 	var text = mxUtils.getTextContent(entry);
+							 	var value = null;
+							 	
+							 	if (text != null && text.length > 0 && mxStylesheetCodec.allowEval)
+							 	{
+							 		value = mxUtils.eval(text);
+							 	}
+							 	else
+							 	{
+							 		value = entry.getAttribute('value');
+							 		
+							 		if (mxUtils.isNumeric(value))
+							 		{
+										value = parseFloat(value);
+									}
+							 	}
+
+							 	if (value != null)
+							 	{
+							 		style[key] = value;
+							 	}
+						 	}
+						 	else if (entry.nodeName == 'remove')
+						 	{
+						 		delete style[key];
+						 	}
+						}
+						
+						entry = entry.nextSibling;
+					}
+					
+					obj.putCellStyle(as, style);
+				}
+			}
+			
+			node = node.nextSibling;
+		}
+		
+		return obj;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
+
+/**
+ * Variable: allowEval
+ * 
+ * Static global switch that specifies if the use of eval is allowed for
+ * evaluating text content. Default is true. Set this to false if stylesheets
+ * may contain user input.
+ */
+mxStylesheetCodec.allowEval = true;
diff --git a/airavata-kubernetes/web-console/src/assets/js/io/mxTerminalChangeCodec.js b/airavata-kubernetes/web-console/src/assets/js/io/mxTerminalChangeCodec.js
new file mode 100644
index 0000000..b65f47f
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/io/mxTerminalChangeCodec.js
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+mxCodecRegistry.register(function()
+{
+	/**
+	 * Class: mxTerminalChangeCodec
+	 *
+	 * Codec for <mxTerminalChange>s. This class is created and registered
+	 * dynamically at load time and used implicitely via <mxCodec> and
+	 * the <mxCodecRegistry>.
+	 *
+	 * Transient Fields:
+	 *
+	 * - model
+	 * - previous
+	 *
+	 * Reference Fields:
+	 *
+	 * - cell
+	 * - terminal
+	 */
+	var codec = new mxObjectCodec(new mxTerminalChange(),
+		['model', 'previous'], ['cell', 'terminal']);
+
+	/**
+	 * Function: afterDecode
+	 *
+	 * Restores the state by assigning the previous value.
+	 */
+	codec.afterDecode = function(dec, node, obj)
+	{
+		obj.previous = obj.terminal;
+		
+		return obj;
+	};
+
+	// Returns the codec into the registry
+	return codec;
+
+}());
diff --git a/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/model/mxGraphAbstractHierarchyCell.js b/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/model/mxGraphAbstractHierarchyCell.js
new file mode 100644
index 0000000..fd81b0b
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/model/mxGraphAbstractHierarchyCell.js
@@ -0,0 +1,206 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGraphAbstractHierarchyCell
+ * 
+ * An abstraction of an internal hierarchy node or edge
+ * 
+ * Constructor: mxGraphAbstractHierarchyCell
+ *
+ * Constructs a new hierarchical layout algorithm.
+ *
+ * Arguments:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * deterministic - Optional boolean that specifies if this layout should be
+ * deterministic. Default is true.
+ */
+function mxGraphAbstractHierarchyCell()
+{
+	this.x = [];
+	this.y = [];
+	this.temp = [];
+};
+
+/**
+ * Variable: maxRank
+ * 
+ * The maximum rank this cell occupies. Default is -1.
+ */
+mxGraphAbstractHierarchyCell.prototype.maxRank = -1;
+
+/**
+ * Variable: minRank
+ * 
+ * The minimum rank this cell occupies. Default is -1.
+ */
+mxGraphAbstractHierarchyCell.prototype.minRank = -1;
+
+/**
+ * Variable: x
+ * 
+ * The x position of this cell for each layer it occupies
+ */
+mxGraphAbstractHierarchyCell.prototype.x = null;
+
+/**
+ * Variable: y
+ * 
+ * The y position of this cell for each layer it occupies
+ */
+mxGraphAbstractHierarchyCell.prototype.y = null;
+
+/**
+ * Variable: width
+ * 
+ * The width of this cell
+ */
+mxGraphAbstractHierarchyCell.prototype.width = 0;
+
+/**
+ * Variable: height
+ * 
+ * The height of this cell
+ */
+mxGraphAbstractHierarchyCell.prototype.height = 0;
+
+/**
+ * Variable: nextLayerConnectedCells
+ * 
+ * A cached version of the cells this cell connects to on the next layer up
+ */
+mxGraphAbstractHierarchyCell.prototype.nextLayerConnectedCells = null;
+
+/**
+ * Variable: previousLayerConnectedCells
+ * 
+ * A cached version of the cells this cell connects to on the next layer down
+ */
+mxGraphAbstractHierarchyCell.prototype.previousLayerConnectedCells = null;
+
+/**
+ * Variable: temp
+ * 
+ * Temporary variable for general use. Generally, try to avoid
+ * carrying information between stages. Currently, the longest
+ * path layering sets temp to the rank position in fixRanks()
+ * and the crossing reduction uses this. This meant temp couldn't
+ * be used for hashing the nodes in the model dfs and so hashCode
+ * was created
+ */
+mxGraphAbstractHierarchyCell.prototype.temp = null;
+
+/**
+ * Function: getNextLayerConnectedCells
+ * 
+ * Returns the cells this cell connects to on the next layer up
+ */
+mxGraphAbstractHierarchyCell.prototype.getNextLayerConnectedCells = function(layer)
+{
+	return null;
+};
+
+/**
+ * Function: getPreviousLayerConnectedCells
+ * 
+ * Returns the cells this cell connects to on the next layer down
+ */
+mxGraphAbstractHierarchyCell.prototype.getPreviousLayerConnectedCells = function(layer)
+{
+	return null;
+};
+
+/**
+ * Function: isEdge
+ * 
+ * Returns whether or not this cell is an edge
+ */
+mxGraphAbstractHierarchyCell.prototype.isEdge = function()
+{
+	return false;
+};
+
+/**
+ * Function: isVertex
+ * 
+ * Returns whether or not this cell is a node
+ */
+mxGraphAbstractHierarchyCell.prototype.isVertex = function()
+{
+	return false;
+};
+
+/**
+ * Function: getGeneralPurposeVariable
+ * 
+ * Gets the value of temp for the specified layer
+ */
+mxGraphAbstractHierarchyCell.prototype.getGeneralPurposeVariable = function(layer)
+{
+	return null;
+};
+
+/**
+ * Function: setGeneralPurposeVariable
+ * 
+ * Set the value of temp for the specified layer
+ */
+mxGraphAbstractHierarchyCell.prototype.setGeneralPurposeVariable = function(layer, value)
+{
+	return null;
+};
+
+/**
+ * Function: setX
+ * 
+ * Set the value of x for the specified layer
+ */
+mxGraphAbstractHierarchyCell.prototype.setX = function(layer, value)
+{
+	if (this.isVertex())
+	{
+		this.x[0] = value;
+	}
+	else if (this.isEdge())
+	{
+		this.x[layer - this.minRank - 1] = value;
+	}
+};
+
+/**
+ * Function: getX
+ * 
+ * Gets the value of x on the specified layer
+ */
+mxGraphAbstractHierarchyCell.prototype.getX = function(layer)
+{
+	if (this.isVertex())
+	{
+		return this.x[0];
+	}
+	else if (this.isEdge())
+	{
+		return this.x[layer - this.minRank - 1];
+	}
+
+	return 0.0;
+};
+
+/**
+ * Function: setY
+ * 
+ * Set the value of y for the specified layer
+ */
+mxGraphAbstractHierarchyCell.prototype.setY = function(layer, value)
+{
+	if (this.isVertex())
+	{
+		this.y[0] = value;
+	}
+	else if (this.isEdge())
+	{
+		this.y[layer -this. minRank - 1] = value;
+	}
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/model/mxGraphHierarchyEdge.js b/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/model/mxGraphHierarchyEdge.js
new file mode 100644
index 0000000..0f81cbb
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/model/mxGraphHierarchyEdge.js
@@ -0,0 +1,187 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGraphHierarchyEdge
+ * 
+ * An abstraction of a hierarchical edge for the hierarchy layout
+ * 
+ * Constructor: mxGraphHierarchyEdge
+ *
+ * Constructs a hierarchy edge
+ *
+ * Arguments:
+ * 
+ * edges - a list of real graph edges this abstraction represents
+ */
+function mxGraphHierarchyEdge(edges)
+{
+	mxGraphAbstractHierarchyCell.apply(this, arguments);
+	this.edges = edges;
+	this.ids = [];
+	
+	for (var i = 0; i < edges.length; i++)
+	{
+		this.ids.push(mxObjectIdentity.get(edges[i]));
+	}
+};
+
+/**
+ * Extends mxGraphAbstractHierarchyCell.
+ */
+mxGraphHierarchyEdge.prototype = new mxGraphAbstractHierarchyCell();
+mxGraphHierarchyEdge.prototype.constructor = mxGraphHierarchyEdge;
+
+/**
+ * Variable: edges
+ * 
+ * The graph edge(s) this object represents. Parallel edges are all grouped
+ * together within one hierarchy edge.
+ */
+mxGraphHierarchyEdge.prototype.edges = null;
+
+/**
+ * Variable: ids
+ * 
+ * The object identities of the wrapped cells
+ */
+mxGraphHierarchyEdge.prototype.ids = null;
+
+/**
+ * Variable: source
+ * 
+ * The node this edge is sourced at
+ */
+mxGraphHierarchyEdge.prototype.source = null;
+
+/**
+ * Variable: target
+ * 
+ * The node this edge targets
+ */
+mxGraphHierarchyEdge.prototype.target = null;
+
+/**
+ * Variable: isReversed
+ * 
+ * Whether or not the direction of this edge has been reversed
+ * internally to create a DAG for the hierarchical layout
+ */
+mxGraphHierarchyEdge.prototype.isReversed = false;
+
+/**
+ * Function: invert
+ * 
+ * Inverts the direction of this internal edge(s)
+ */
+mxGraphHierarchyEdge.prototype.invert = function(layer)
+{
+	var temp = this.source;
+	this.source = this.target;
+	this.target = temp;
+	this.isReversed = !this.isReversed;
+};
+
+/**
+ * Function: getNextLayerConnectedCells
+ * 
+ * Returns the cells this cell connects to on the next layer up
+ */
+mxGraphHierarchyEdge.prototype.getNextLayerConnectedCells = function(layer)
+{
+	if (this.nextLayerConnectedCells == null)
+	{
+		this.nextLayerConnectedCells = [];
+		
+		for (var i = 0; i < this.temp.length; i++)
+		{
+			this.nextLayerConnectedCells[i] = [];
+			
+			if (i == this.temp.length - 1)
+			{
+				this.nextLayerConnectedCells[i].push(this.source);
+			}
+			else
+			{
+				this.nextLayerConnectedCells[i].push(this);
+			}
+		}
+	}
+	
+	return this.nextLayerConnectedCells[layer - this.minRank - 1];
+};
+
+/**
+ * Function: getPreviousLayerConnectedCells
+ * 
+ * Returns the cells this cell connects to on the next layer down
+ */
+mxGraphHierarchyEdge.prototype.getPreviousLayerConnectedCells = function(layer)
+{
+	if (this.previousLayerConnectedCells == null)
+	{
+		this.previousLayerConnectedCells = [];
+
+		for (var i = 0; i < this.temp.length; i++)
+		{
+			this.previousLayerConnectedCells[i] = [];
+			
+			if (i == 0)
+			{
+				this.previousLayerConnectedCells[i].push(this.target);
+			}
+			else
+			{
+				this.previousLayerConnectedCells[i].push(this);
+			}
+		}
+	}
+
+	return this.previousLayerConnectedCells[layer - this.minRank - 1];
+};
+
+/**
+ * Function: isEdge
+ * 
+ * Returns true.
+ */
+mxGraphHierarchyEdge.prototype.isEdge = function()
+{
+	return true;
+};
+
+/**
+ * Function: getGeneralPurposeVariable
+ * 
+ * Gets the value of temp for the specified layer
+ */
+mxGraphHierarchyEdge.prototype.getGeneralPurposeVariable = function(layer)
+{
+	return this.temp[layer - this.minRank - 1];
+};
+
+/**
+ * Function: setGeneralPurposeVariable
+ * 
+ * Set the value of temp for the specified layer
+ */
+mxGraphHierarchyEdge.prototype.setGeneralPurposeVariable = function(layer, value)
+{
+	this.temp[layer - this.minRank - 1] = value;
+};
+
+/**
+ * Function: getCoreCell
+ * 
+ * Gets the first core edge associated with this wrapper
+ */
+mxGraphHierarchyEdge.prototype.getCoreCell = function()
+{
+	if (this.edges != null && this.edges.length > 0)
+	{
+		return this.edges[0];
+	}
+	
+	return null;
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/model/mxGraphHierarchyModel.js b/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/model/mxGraphHierarchyModel.js
new file mode 100644
index 0000000..de37e09
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/model/mxGraphHierarchyModel.js
@@ -0,0 +1,681 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGraphHierarchyModel
+ *
+ * Internal model of a hierarchical graph. This model stores nodes and edges
+ * equivalent to the real graph nodes and edges, but also stores the rank of the
+ * cells, the order within the ranks and the new candidate locations of cells.
+ * The internal model also reverses edge direction were appropriate , ignores
+ * self-loop and groups parallels together under one edge object.
+ *
+ * Constructor: mxGraphHierarchyModel
+ *
+ * Creates an internal ordered graph model using the vertices passed in. If
+ * there are any, leftward edge need to be inverted in the internal model
+ *
+ * Arguments:
+ *
+ * graph - the facade describing the graph to be operated on
+ * vertices - the vertices for this hierarchy
+ * ordered - whether or not the vertices are already ordered
+ * deterministic - whether or not this layout should be deterministic on each
+ * tightenToSource - whether or not to tighten vertices towards the sources
+ * scanRanksFromSinks - Whether rank assignment is from the sinks or sources.
+ * usage
+ */
+function mxGraphHierarchyModel(layout, vertices, roots, parent, tightenToSource)
+{
+	var graph = layout.getGraph();
+	this.tightenToSource = tightenToSource;
+	this.roots = roots;
+	this.parent = parent;
+
+	// map of cells to internal cell needed for second run through
+	// to setup the sink of edges correctly
+	this.vertexMapper = new mxDictionary();
+	this.edgeMapper = new mxDictionary();
+	this.maxRank = 0;
+	var internalVertices = [];
+
+	if (vertices == null)
+	{
+		vertices = this.graph.getChildVertices(parent);
+	}
+
+	this.maxRank = this.SOURCESCANSTARTRANK;
+	// map of cells to internal cell needed for second run through
+	// to setup the sink of edges correctly. Guess size by number
+	// of edges is roughly same as number of vertices.
+	this.createInternalCells(layout, vertices, internalVertices);
+
+	// Go through edges set their sink values. Also check the
+	// ordering if and invert edges if necessary
+	for (var i = 0; i < vertices.length; i++)
+	{
+		var edges = internalVertices[i].connectsAsSource;
+
+		for (var j = 0; j < edges.length; j++)
+		{
+			var internalEdge = edges[j];
+			var realEdges = internalEdge.edges;
+
+			// Only need to process the first real edge, since
+			// all the edges connect to the same other vertex
+			if (realEdges != null && realEdges.length > 0)
+			{
+				var realEdge = realEdges[0];
+				var targetCell = layout.getVisibleTerminal(
+						realEdge, false);
+				var internalTargetCell = this.vertexMapper.get(targetCell);
+
+				if (internalVertices[i] == internalTargetCell)
+				{
+					// If there are parallel edges going between two vertices and not all are in the same direction
+					// you can have navigated across one direction when doing the cycle reversal that isn't the same
+					// direction as the first real edge in the array above. When that happens the if above catches
+					// that and we correct the target cell before continuing.
+					// This branch only detects this single case
+					targetCell = layout.getVisibleTerminal(
+							realEdge, true);
+					internalTargetCell = this.vertexMapper.get(targetCell);
+				}
+				
+				if (internalTargetCell != null
+						&& internalVertices[i] != internalTargetCell)
+				{
+					internalEdge.target = internalTargetCell;
+
+					if (internalTargetCell.connectsAsTarget.length == 0)
+					{
+						internalTargetCell.connectsAsTarget = [];
+					}
+
+					if (mxUtils.indexOf(internalTargetCell.connectsAsTarget, internalEdge) < 0)
+					{
+						internalTargetCell.connectsAsTarget.push(internalEdge);
+					}
+				}
+			}
+		}
+
+		// Use the temp variable in the internal nodes to mark this
+		// internal vertex as having been visited.
+		internalVertices[i].temp[0] = 1;
+	}
+};
+
+/**
+ * Variable: maxRank
+ *
+ * Stores the largest rank number allocated
+ */
+mxGraphHierarchyModel.prototype.maxRank = null;
+
+/**
+ * Variable: vertexMapper
+ *
+ * Map from graph vertices to internal model nodes.
+ */
+mxGraphHierarchyModel.prototype.vertexMapper = null;
+
+/**
+ * Variable: edgeMapper
+ *
+ * Map from graph edges to internal model edges
+ */
+mxGraphHierarchyModel.prototype.edgeMapper = null;
+
+/**
+ * Variable: ranks
+ *
+ * Mapping from rank number to actual rank
+ */
+mxGraphHierarchyModel.prototype.ranks = null;
+
+/**
+ * Variable: roots
+ *
+ * Store of roots of this hierarchy model, these are real graph cells, not
+ * internal cells
+ */
+mxGraphHierarchyModel.prototype.roots = null;
+
+/**
+ * Variable: parent
+ *
+ * The parent cell whose children are being laid out
+ */
+mxGraphHierarchyModel.prototype.parent = null;
+
+/**
+ * Variable: dfsCount
+ *
+ * Count of the number of times the ancestor dfs has been used.
+ */
+mxGraphHierarchyModel.prototype.dfsCount = 0;
+
+/**
+ * Variable: SOURCESCANSTARTRANK
+ *
+ * High value to start source layering scan rank value from.
+ */
+mxGraphHierarchyModel.prototype.SOURCESCANSTARTRANK = 100000000;
+
+/**
+ * Variable: tightenToSource
+ *
+ * Whether or not to tighten the assigned ranks of vertices up towards
+ * the source cells.
+ */
+mxGraphHierarchyModel.prototype.tightenToSource = false;
+
+/**
+ * Function: createInternalCells
+ *
+ * Creates all edges in the internal model
+ *
+ * Parameters:
+ *
+ * layout - Reference to the <mxHierarchicalLayout> algorithm.
+ * vertices - Array of <mxCells> that represent the vertices whom are to
+ * have an internal representation created.
+ * internalVertices - The array of <mxGraphHierarchyNodes> to have their
+ * information filled in using the real vertices.
+ */
+mxGraphHierarchyModel.prototype.createInternalCells = function(layout, vertices, internalVertices)
+{
+	var graph = layout.getGraph();
+
+	// Create internal edges
+	for (var i = 0; i < vertices.length; i++)
+	{
+		internalVertices[i] = new mxGraphHierarchyNode(vertices[i]);
+		this.vertexMapper.put(vertices[i], internalVertices[i]);
+
+		// If the layout is deterministic, order the cells
+		//List outgoingCells = graph.getNeighbours(vertices[i], deterministic);
+		var conns = layout.getEdges(vertices[i]);
+		internalVertices[i].connectsAsSource = [];
+
+		// Create internal edges, but don't do any rank assignment yet
+		// First use the information from the greedy cycle remover to
+		// invert the leftward edges internally
+		for (var j = 0; j < conns.length; j++)
+		{
+			var cell = layout.getVisibleTerminal(conns[j], false);
+
+			// Looking for outgoing edges only
+			if (cell != vertices[i] && layout.graph.model.isVertex(cell) &&
+					!layout.isVertexIgnored(cell))
+			{
+				// We process all edge between this source and its targets
+				// If there are edges going both ways, we need to collect
+				// them all into one internal edges to avoid looping problems
+				// later. We assume this direction (source -> target) is the 
+				// natural direction if at least half the edges are going in
+				// that direction.
+
+				// The check below for edges[0] being in the vertex mapper is
+				// in case we've processed this the other way around
+				// (target -> source) and the number of edges in each direction
+				// are the same. All the graph edges will have been assigned to
+				// an internal edge going the other way, so we don't want to 
+				// process them again
+				var undirectedEdges = layout.getEdgesBetween(vertices[i],
+						cell, false);
+				var directedEdges = layout.getEdgesBetween(vertices[i],
+						cell, true);
+				
+				if (undirectedEdges != null &&
+						undirectedEdges.length > 0 &&
+						this.edgeMapper.get(undirectedEdges[0]) == null &&
+						directedEdges.length * 2 >= undirectedEdges.length)
+				{
+					var internalEdge = new mxGraphHierarchyEdge(undirectedEdges);
+
+					for (var k = 0; k < undirectedEdges.length; k++)
+					{
+						var edge = undirectedEdges[k];
+						this.edgeMapper.put(edge, internalEdge);
+
+						// Resets all point on the edge and disables the edge style
+						// without deleting it from the cell style
+						graph.resetEdge(edge);
+
+					    if (layout.disableEdgeStyle)
+					    {
+					    	layout.setEdgeStyleEnabled(edge, false);
+					    	layout.setOrthogonalEdge(edge,true);
+					    }
+					}
+
+					internalEdge.source = internalVertices[i];
+
+					if (mxUtils.indexOf(internalVertices[i].connectsAsSource, internalEdge) < 0)
+					{
+						internalVertices[i].connectsAsSource.push(internalEdge);
+					}
+				}
+			}
+		}
+
+		// Ensure temp variable is cleared from any previous use
+		internalVertices[i].temp[0] = 0;
+	}
+};
+
+/**
+ * Function: initialRank
+ *
+ * Basic determination of minimum layer ranking by working from from sources
+ * or sinks and working through each node in the relevant edge direction.
+ * Starting at the sinks is basically a longest path layering algorithm.
+*/
+mxGraphHierarchyModel.prototype.initialRank = function()
+{
+	var startNodes = [];
+
+	if (this.roots != null)
+	{
+		for (var i = 0; i < this.roots.length; i++)
+		{
+			var internalNode = this.vertexMapper.get(this.roots[i]);
+
+			if (internalNode != null)
+			{
+				startNodes.push(internalNode);
+			}
+		}
+	}
+
+	var internalNodes = this.vertexMapper.getValues();
+	
+	for (var i=0; i < internalNodes.length; i++)
+	{
+		// Mark the node as not having had a layer assigned
+		internalNodes[i].temp[0] = -1;
+	}
+
+	var startNodesCopy = startNodes.slice();
+
+	while (startNodes.length > 0)
+	{
+		var internalNode = startNodes[0];
+		var layerDeterminingEdges;
+		var edgesToBeMarked;
+
+		layerDeterminingEdges = internalNode.connectsAsTarget;
+		edgesToBeMarked = internalNode.connectsAsSource;
+
+		// flag to keep track of whether or not all layer determining
+		// edges have been scanned
+		var allEdgesScanned = true;
+
+		// Work out the layer of this node from the layer determining
+		// edges. The minimum layer number of any node connected by one of
+		// the layer determining edges variable
+		var minimumLayer = this.SOURCESCANSTARTRANK;
+
+		for (var i = 0; i < layerDeterminingEdges.length; i++)
+		{
+			var internalEdge = layerDeterminingEdges[i];
+
+			if (internalEdge.temp[0] == 5270620)
+			{
+				// This edge has been scanned, get the layer of the
+				// node on the other end
+				var otherNode = internalEdge.source;
+				minimumLayer = Math.min(minimumLayer, otherNode.temp[0] - 1);
+			}
+			else
+			{
+				allEdgesScanned = false;
+
+				break;
+			}
+		}
+
+		// If all edge have been scanned, assign the layer, mark all
+		// edges in the other direction and remove from the nodes list
+		if (allEdgesScanned)
+		{
+			internalNode.temp[0] = minimumLayer;
+			this.maxRank = Math.min(this.maxRank, minimumLayer);
+
+			if (edgesToBeMarked != null)
+			{
+				for (var i = 0; i < edgesToBeMarked.length; i++)
+				{
+					var internalEdge = edgesToBeMarked[i];
+
+					// Assign unique stamp ( y/m/d/h )
+					internalEdge.temp[0] = 5270620;
+
+					// Add node on other end of edge to LinkedList of
+					// nodes to be analysed
+					var otherNode = internalEdge.target;
+
+					// Only add node if it hasn't been assigned a layer
+					if (otherNode.temp[0] == -1)
+					{
+						startNodes.push(otherNode);
+
+						// Mark this other node as neither being
+						// unassigned nor assigned so it isn't
+						// added to this list again, but it's
+						// layer isn't used in any calculation.
+						otherNode.temp[0] = -2;
+					}
+				}
+			}
+
+			startNodes.shift();
+		}
+		else
+		{
+			// Not all the edges have been scanned, get to the back of
+			// the class and put the dunces cap on
+			var removedCell = startNodes.shift();
+			startNodes.push(internalNode);
+
+			if (removedCell == internalNode && startNodes.length == 1)
+			{
+				// This is an error condition, we can't get out of
+				// this loop. It could happen for more than one node
+				// but that's a lot harder to detect. Log the error
+				// TODO make log comment
+				break;
+			}
+		}
+	}
+
+	// Normalize the ranks down from their large starting value to place
+	// at least 1 sink on layer 0
+	for (var i=0; i < internalNodes.length; i++)
+	{
+		// Mark the node as not having had a layer assigned
+		internalNodes[i].temp[0] -= this.maxRank;
+	}
+	
+	// Tighten the rank 0 nodes as far as possible
+	for ( var i = 0; i < startNodesCopy.length; i++)
+	{
+		var internalNode = startNodesCopy[i];
+		var currentMaxLayer = 0;
+		var layerDeterminingEdges = internalNode.connectsAsSource;
+
+		for ( var j = 0; j < layerDeterminingEdges.length; j++)
+		{
+			var internalEdge = layerDeterminingEdges[j];
+			var otherNode = internalEdge.target;
+			internalNode.temp[0] = Math.max(currentMaxLayer,
+					otherNode.temp[0] + 1);
+			currentMaxLayer = internalNode.temp[0];
+		}
+	}
+	
+	// Reset the maxRank to that which would be expected for a from-sink
+	// scan
+	this.maxRank = this.SOURCESCANSTARTRANK - this.maxRank;
+};
+
+/**
+ * Function: fixRanks
+ *
+ * Fixes the layer assignments to the values stored in the nodes. Also needs
+ * to create dummy nodes for edges that cross layers.
+ */
+mxGraphHierarchyModel.prototype.fixRanks = function()
+{
+	var rankList = [];
+	this.ranks = [];
+
+	for (var i = 0; i < this.maxRank + 1; i++)
+	{
+		rankList[i] = [];
+		this.ranks[i] = rankList[i];
+	}
+
+	// Perform a DFS to obtain an initial ordering for each rank.
+	// Without doing this you would end up having to process
+	// crossings for a standard tree.
+	var rootsArray = null;
+
+	if (this.roots != null)
+	{
+		var oldRootsArray = this.roots;
+		rootsArray = [];
+
+		for (var i = 0; i < oldRootsArray.length; i++)
+		{
+			var cell = oldRootsArray[i];
+			var internalNode = this.vertexMapper.get(cell);
+			rootsArray[i] = internalNode;
+		}
+	}
+
+	this.visit(function(parent, node, edge, layer, seen)
+	{
+		if (seen == 0 && node.maxRank < 0 && node.minRank < 0)
+		{
+			rankList[node.temp[0]].push(node);
+			node.maxRank = node.temp[0];
+			node.minRank = node.temp[0];
+
+			// Set temp[0] to the nodes position in the rank
+			node.temp[0] = rankList[node.maxRank].length - 1;
+		}
+
+		if (parent != null && edge != null)
+		{
+			var parentToCellRankDifference = parent.maxRank - node.maxRank;
+
+			if (parentToCellRankDifference > 1)
+			{
+				// There are ranks in between the parent and current cell
+				edge.maxRank = parent.maxRank;
+				edge.minRank = node.maxRank;
+				edge.temp = [];
+				edge.x = [];
+				edge.y = [];
+
+				for (var i = edge.minRank + 1; i < edge.maxRank; i++)
+				{
+					// The connecting edge must be added to the
+					// appropriate ranks
+					rankList[i].push(edge);
+					edge.setGeneralPurposeVariable(i, rankList[i]
+							.length - 1);
+				}
+			}
+		}
+	}, rootsArray, false, null);
+};
+
+/**
+ * Function: visit
+ *
+ * A depth first search through the internal heirarchy model.
+ *
+ * Parameters:
+ *
+ * visitor - The visitor function pattern to be called for each node.
+ * trackAncestors - Whether or not the search is to keep track all nodes
+ * directly above this one in the search path.
+ */
+mxGraphHierarchyModel.prototype.visit = function(visitor, dfsRoots, trackAncestors, seenNodes)
+{
+	// Run dfs through on all roots
+	if (dfsRoots != null)
+	{
+		for (var i = 0; i < dfsRoots.length; i++)
+		{
+			var internalNode = dfsRoots[i];
+
+			if (internalNode != null)
+			{
+				if (seenNodes == null)
+				{
+					seenNodes = new Object();
+				}
+
+				if (trackAncestors)
+				{
+					// Set up hash code for root
+					internalNode.hashCode = [];
+					internalNode.hashCode[0] = this.dfsCount;
+					internalNode.hashCode[1] = i;
+					this.extendedDfs(null, internalNode, null, visitor, seenNodes,
+							internalNode.hashCode, i, 0);
+				}
+				else
+				{
+					this.dfs(null, internalNode, null, visitor, seenNodes, 0);
+				}
+			}
+		}
+
+		this.dfsCount++;
+	}
+};
+
+/**
+ * Function: dfs
+ *
+ * Performs a depth first search on the internal hierarchy model
+ *
+ * Parameters:
+ *
+ * parent - the parent internal node of the current internal node
+ * root - the current internal node
+ * connectingEdge - the internal edge connecting the internal node and the parent
+ * internal node, if any
+ * visitor - the visitor pattern to be called for each node
+ * seen - a set of all nodes seen by this dfs a set of all of the
+ * ancestor node of the current node
+ * layer - the layer on the dfs tree ( not the same as the model ranks )
+ */
+mxGraphHierarchyModel.prototype.dfs = function(parent, root, connectingEdge, visitor, seen, layer)
+{
+	if (root != null)
+	{
+		var rootId = root.id;
+
+		if (seen[rootId] == null)
+		{
+			seen[rootId] = root;
+			visitor(parent, root, connectingEdge, layer, 0);
+
+			// Copy the connects as source list so that visitors
+			// can change the original for edge direction inversions
+			var outgoingEdges = root.connectsAsSource.slice();
+			
+			for (var i = 0; i< outgoingEdges.length; i++)
+			{
+				var internalEdge = outgoingEdges[i];
+				var targetNode = internalEdge.target;
+
+				// Root check is O(|roots|)
+				this.dfs(root, targetNode, internalEdge, visitor, seen,
+						layer + 1);
+			}
+		}
+		else
+		{
+			// Use the int field to indicate this node has been seen
+			visitor(parent, root, connectingEdge, layer, 1);
+		}
+	}
+};
+
+/**
+ * Function: extendedDfs
+ *
+ * Performs a depth first search on the internal hierarchy model. This dfs
+ * extends the default version by keeping track of cells ancestors, but it
+ * should be only used when necessary because of it can be computationally
+ * intensive for deep searches.
+ *
+ * Parameters:
+ *
+ * parent - the parent internal node of the current internal node
+ * root - the current internal node
+ * connectingEdge - the internal edge connecting the internal node and the parent
+ * internal node, if any
+ * visitor - the visitor pattern to be called for each node
+ * seen - a set of all nodes seen by this dfs
+ * ancestors - the parent hash code
+ * childHash - the new hash code for this node
+ * layer - the layer on the dfs tree ( not the same as the model ranks )
+ */
+mxGraphHierarchyModel.prototype.extendedDfs = function(parent, root, connectingEdge, visitor, seen, ancestors, childHash, layer)
+{
+	// Explanation of custom hash set. Previously, the ancestors variable
+	// was passed through the dfs as a HashSet. The ancestors were copied
+	// into a new HashSet and when the new child was processed it was also
+	// added to the set. If the current node was in its ancestor list it
+	// meant there is a cycle in the graph and this information is passed
+	// to the visitor.visit() in the seen parameter. The HashSet clone was
+	// very expensive on CPU so a custom hash was developed using primitive
+	// types. temp[] couldn't be used so hashCode[] was added to each node.
+	// Each new child adds another int to the array, copying the prefix
+	// from its parent. Child of the same parent add different ints (the
+	// limit is therefore 2^32 children per parent...). If a node has a
+	// child with the hashCode already set then the child code is compared
+	// to the same portion of the current nodes array. If they match there
+	// is a loop.
+	// Note that the basic mechanism would only allow for 1 use of this
+	// functionality, so the root nodes have two ints. The second int is
+	// incremented through each node root and the first is incremented
+	// through each run of the dfs algorithm (therefore the dfs is not
+	// thread safe). The hash code of each node is set if not already set,
+	// or if the first int does not match that of the current run.
+	if (root != null)
+	{
+		if (parent != null)
+		{
+			// Form this nodes hash code if necessary, that is, if the
+			// hashCode variable has not been initialized or if the
+			// start of the parent hash code does not equal the start of
+			// this nodes hash code, indicating the code was set on a
+			// previous run of this dfs.
+			if (root.hashCode == null ||
+				root.hashCode[0] != parent.hashCode[0])
+			{
+				var hashCodeLength = parent.hashCode.length + 1;
+				root.hashCode = parent.hashCode.slice();
+				root.hashCode[hashCodeLength - 1] = childHash;
+			}
+		}
+
+		var rootId = root.id;
+
+		if (seen[rootId] == null)
+		{
+			seen[rootId] = root;
+			visitor(parent, root, connectingEdge, layer, 0);
+
+			// Copy the connects as source list so that visitors
+			// can change the original for edge direction inversions
+			var outgoingEdges = root.connectsAsSource.slice();
+
+			for (var i = 0; i < outgoingEdges.length; i++)
+			{
+				var internalEdge = outgoingEdges[i];
+				var targetNode = internalEdge.target;
+
+				// Root check is O(|roots|)
+				this.extendedDfs(root, targetNode, internalEdge, visitor, seen,
+						root.hashCode, i, layer + 1);
+			}
+		}
+		else
+		{
+			// Use the int field to indicate this node has been seen
+			visitor(parent, root, connectingEdge, layer, 1);
+		}
+	}
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/model/mxGraphHierarchyNode.js b/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/model/mxGraphHierarchyNode.js
new file mode 100644
index 0000000..1dbb8be
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/model/mxGraphHierarchyNode.js
@@ -0,0 +1,220 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxGraphHierarchyNode
+ * 
+ * An abstraction of a hierarchical edge for the hierarchy layout
+ * 
+ * Constructor: mxGraphHierarchyNode
+ *
+ * Constructs an internal node to represent the specified real graph cell
+ *
+ * Arguments:
+ * 
+ * cell - the real graph cell this node represents
+ */
+function mxGraphHierarchyNode(cell)
+{
+	mxGraphAbstractHierarchyCell.apply(this, arguments);
+	this.cell = cell;
+	this.id = mxObjectIdentity.get(cell);
+	this.connectsAsTarget = [];
+	this.connectsAsSource = [];
+};
+
+/**
+ * Extends mxGraphAbstractHierarchyCell.
+ */
+mxGraphHierarchyNode.prototype = new mxGraphAbstractHierarchyCell();
+mxGraphHierarchyNode.prototype.constructor = mxGraphHierarchyNode;
+
+/**
+ * Variable: cell
+ * 
+ * The graph cell this object represents.
+ */
+mxGraphHierarchyNode.prototype.cell = null;
+
+/**
+ * Variable: id
+ * 
+ * The object identity of the wrapped cell
+ */
+mxGraphHierarchyNode.prototype.id = null;
+
+/**
+ * Variable: connectsAsTarget
+ * 
+ * Collection of hierarchy edges that have this node as a target
+ */
+mxGraphHierarchyNode.prototype.connectsAsTarget = null;
+
+/**
+ * Variable: connectsAsSource
+ * 
+ * Collection of hierarchy edges that have this node as a source
+ */
+mxGraphHierarchyNode.prototype.connectsAsSource = null;
+
+/**
+ * Variable: hashCode
+ * 
+ * Assigns a unique hashcode for each node. Used by the model dfs instead
+ * of copying HashSets
+ */
+mxGraphHierarchyNode.prototype.hashCode = false;
+
+/**
+ * Function: getRankValue
+ * 
+ * Returns the integer value of the layer that this node resides in
+ */
+mxGraphHierarchyNode.prototype.getRankValue = function(layer)
+{
+	return this.maxRank;
+};
+
+/**
+ * Function: getNextLayerConnectedCells
+ * 
+ * Returns the cells this cell connects to on the next layer up
+ */
+mxGraphHierarchyNode.prototype.getNextLayerConnectedCells = function(layer)
+{
+	if (this.nextLayerConnectedCells == null)
+	{
+		this.nextLayerConnectedCells = [];
+		this.nextLayerConnectedCells[0] = [];
+		
+		for (var i = 0; i < this.connectsAsTarget.length; i++)
+		{
+			var edge = this.connectsAsTarget[i];
+
+			if (edge.maxRank == -1 || edge.maxRank == layer + 1)
+			{
+				// Either edge is not in any rank or
+				// no dummy nodes in edge, add node of other side of edge
+				this.nextLayerConnectedCells[0].push(edge.source);
+			}
+			else
+			{
+				// Edge spans at least two layers, add edge
+				this.nextLayerConnectedCells[0].push(edge);
+			}
+		}
+	}
+
+	return this.nextLayerConnectedCells[0];
+};
+
+/**
+ * Function: getPreviousLayerConnectedCells
+ * 
+ * Returns the cells this cell connects to on the next layer down
+ */
+mxGraphHierarchyNode.prototype.getPreviousLayerConnectedCells = function(layer)
+{
+	if (this.previousLayerConnectedCells == null)
+	{
+		this.previousLayerConnectedCells = [];
+		this.previousLayerConnectedCells[0] = [];
+		
+		for (var i = 0; i < this.connectsAsSource.length; i++)
+		{
+			var edge = this.connectsAsSource[i];
+
+			if (edge.minRank == -1 || edge.minRank == layer - 1)
+			{
+				// No dummy nodes in edge, add node of other side of edge
+				this.previousLayerConnectedCells[0].push(edge.target);
+			}
+			else
+			{
+				// Edge spans at least two layers, add edge
+				this.previousLayerConnectedCells[0].push(edge);
+			}
+		}
+	}
+
+	return this.previousLayerConnectedCells[0];
+};
+
+/**
+ * Function: isVertex
+ * 
+ * Returns true.
+ */
+mxGraphHierarchyNode.prototype.isVertex = function()
+{
+	return true;
+};
+
+/**
+ * Function: getGeneralPurposeVariable
+ * 
+ * Gets the value of temp for the specified layer
+ */
+mxGraphHierarchyNode.prototype.getGeneralPurposeVariable = function(layer)
+{
+	return this.temp[0];
+};
+
+/**
+ * Function: setGeneralPurposeVariable
+ * 
+ * Set the value of temp for the specified layer
+ */
+mxGraphHierarchyNode.prototype.setGeneralPurposeVariable = function(layer, value)
+{
+	this.temp[0] = value;
+};
+
+/**
+ * Function: isAncestor
+ */
+mxGraphHierarchyNode.prototype.isAncestor = function(otherNode)
+{
+	// Firstly, the hash code of this node needs to be shorter than the
+	// other node
+	if (otherNode != null && this.hashCode != null && otherNode.hashCode != null
+			&& this.hashCode.length < otherNode.hashCode.length)
+	{
+		if (this.hashCode == otherNode.hashCode)
+		{
+			return true;
+		}
+		
+		if (this.hashCode == null || this.hashCode == null)
+		{
+			return false;
+		}
+		
+		// Secondly, this hash code must match the start of the other
+		// node's hash code. Arrays.equals cannot be used here since
+		// the arrays are different length, and we do not want to
+		// perform another array copy.
+		for (var i = 0; i < this.hashCode.length; i++)
+		{
+			if (this.hashCode[i] != otherNode.hashCode[i])
+			{
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	return false;
+};
+
+/**
+ * Function: getCoreCell
+ * 
+ * Gets the core vertex associated with this wrapper
+ */
+mxGraphHierarchyNode.prototype.getCoreCell = function()
+{
+	return this.cell;
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/model/mxSwimlaneModel.js b/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/model/mxSwimlaneModel.js
new file mode 100644
index 0000000..52873c6
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/model/mxSwimlaneModel.js
@@ -0,0 +1,801 @@
+/**
+ * Copyright (c) 2006-2016, JGraph Ltd
+ * Copyright (c) 2006-2016, Gaudenz Alder
+ */
+/**
+ * Class: mxSwimlaneModel
+ *
+ * Internal model of a hierarchical graph. This model stores nodes and edges
+ * equivalent to the real graph nodes and edges, but also stores the rank of the
+ * cells, the order within the ranks and the new candidate locations of cells.
+ * The internal model also reverses edge direction were appropriate , ignores
+ * self-loop and groups parallels together under one edge object.
+ *
+ * Constructor: mxSwimlaneModel
+ *
+ * Creates an internal ordered graph model using the vertices passed in. If
+ * there are any, leftward edge need to be inverted in the internal model
+ *
+ * Arguments:
+ *
+ * graph - the facade describing the graph to be operated on
+ * vertices - the vertices for this hierarchy
+ * ordered - whether or not the vertices are already ordered
+ * deterministic - whether or not this layout should be deterministic on each
+ * tightenToSource - whether or not to tighten vertices towards the sources
+ * scanRanksFromSinks - Whether rank assignment is from the sinks or sources.
+ * usage
+ */
+function mxSwimlaneModel(layout, vertices, roots, parent, tightenToSource)
+{
+	var graph = layout.getGraph();
+	this.tightenToSource = tightenToSource;
+	this.roots = roots;
+	this.parent = parent;
+
+	// map of cells to internal cell needed for second run through
+	// to setup the sink of edges correctly
+	this.vertexMapper = new mxDictionary();
+	this.edgeMapper = new mxDictionary();
+	this.maxRank = 0;
+	var internalVertices = [];
+
+	if (vertices == null)
+	{
+		vertices = this.graph.getChildVertices(parent);
+	}
+
+	this.maxRank = this.SOURCESCANSTARTRANK;
+	// map of cells to internal cell needed for second run through
+	// to setup the sink of edges correctly. Guess size by number
+	// of edges is roughly same as number of vertices.
+	this.createInternalCells(layout, vertices, internalVertices);
+
+	// Go through edges set their sink values. Also check the
+	// ordering if and invert edges if necessary
+	for (var i = 0; i < vertices.length; i++)
+	{
+		var edges = internalVertices[i].connectsAsSource;
+
+		for (var j = 0; j < edges.length; j++)
+		{
+			var internalEdge = edges[j];
+			var realEdges = internalEdge.edges;
+
+			// Only need to process the first real edge, since
+			// all the edges connect to the same other vertex
+			if (realEdges != null && realEdges.length > 0)
+			{
+				var realEdge = realEdges[0];
+				var targetCell = layout.getVisibleTerminal(
+						realEdge, false);
+				var internalTargetCell = this.vertexMapper.get(targetCell);
+
+				if (internalVertices[i] == internalTargetCell)
+				{
+					// If there are parallel edges going between two vertices and not all are in the same direction
+					// you can have navigated across one direction when doing the cycle reversal that isn't the same
+					// direction as the first real edge in the array above. When that happens the if above catches
+					// that and we correct the target cell before continuing.
+					// This branch only detects this single case
+					targetCell = layout.getVisibleTerminal(
+							realEdge, true);
+					internalTargetCell = this.vertexMapper.get(targetCell);
+				}
+
+				if (internalTargetCell != null
+						&& internalVertices[i] != internalTargetCell)
+				{
+					internalEdge.target = internalTargetCell;
+
+					if (internalTargetCell.connectsAsTarget.length == 0)
+					{
+						internalTargetCell.connectsAsTarget = [];
+					}
+
+					if (mxUtils.indexOf(internalTargetCell.connectsAsTarget, internalEdge) < 0)
+					{
+						internalTargetCell.connectsAsTarget.push(internalEdge);
+					}
+				}
+			}
+		}
+
+		// Use the temp variable in the internal nodes to mark this
+		// internal vertex as having been visited.
+		internalVertices[i].temp[0] = 1;
+	}
+};
+
+/**
+ * Variable: maxRank
+ *
+ * Stores the largest rank number allocated
+ */
+mxSwimlaneModel.prototype.maxRank = null;
+
+/**
+ * Variable: vertexMapper
+ *
+ * Map from graph vertices to internal model nodes.
+ */
+mxSwimlaneModel.prototype.vertexMapper = null;
+
+/**
+ * Variable: edgeMapper
+ *
+ * Map from graph edges to internal model edges
+ */
+mxSwimlaneModel.prototype.edgeMapper = null;
+
+/**
+ * Variable: ranks
+ *
+ * Mapping from rank number to actual rank
+ */
+mxSwimlaneModel.prototype.ranks = null;
+
+/**
+ * Variable: roots
+ *
+ * Store of roots of this hierarchy model, these are real graph cells, not
+ * internal cells
+ */
+mxSwimlaneModel.prototype.roots = null;
+
+/**
+ * Variable: parent
+ *
+ * The parent cell whose children are being laid out
+ */
+mxSwimlaneModel.prototype.parent = null;
+
+/**
+ * Variable: dfsCount
+ *
+ * Count of the number of times the ancestor dfs has been used.
+ */
+mxSwimlaneModel.prototype.dfsCount = 0;
+
+/**
+ * Variable: SOURCESCANSTARTRANK
+ *
+ * High value to start source layering scan rank value from.
+ */
+mxSwimlaneModel.prototype.SOURCESCANSTARTRANK = 100000000;
+
+/**
+ * Variable: tightenToSource
+ *
+ * Whether or not to tighten the assigned ranks of vertices up towards
+ * the source cells.
+ */
+mxGraphHierarchyModel.prototype.tightenToSource = false;
+
+/**
+ * Variable: ranksPerGroup
+ *
+ * An array of the number of ranks within each swimlane
+ */
+mxSwimlaneModel.prototype.ranksPerGroup = null;
+
+/**
+ * Function: createInternalCells
+ *
+ * Creates all edges in the internal model
+ *
+ * Parameters:
+ *
+ * layout - Reference to the <mxHierarchicalLayout> algorithm.
+ * vertices - Array of <mxCells> that represent the vertices whom are to
+ * have an internal representation created.
+ * internalVertices - The array of <mxGraphHierarchyNodes> to have their
+ * information filled in using the real vertices.
+ */
+mxSwimlaneModel.prototype.createInternalCells = function(layout, vertices, internalVertices)
+{
+	var graph = layout.getGraph();
+	var swimlanes = layout.swimlanes;
+
+	// Create internal edges
+	for (var i = 0; i < vertices.length; i++)
+	{
+		internalVertices[i] = new mxGraphHierarchyNode(vertices[i]);
+		this.vertexMapper.put(vertices[i], internalVertices[i]);
+		internalVertices[i].swimlaneIndex = -1;
+
+		for (var ii = 0; ii < swimlanes.length; ii++)
+		{
+			if (graph.model.getParent(vertices[i]) == swimlanes[ii])
+			{
+				internalVertices[i].swimlaneIndex = ii;
+				break;
+			}
+		}
+
+		// If the layout is deterministic, order the cells
+		//List outgoingCells = graph.getNeighbours(vertices[i], deterministic);
+		var conns = layout.getEdges(vertices[i]);
+		internalVertices[i].connectsAsSource = [];
+
+		// Create internal edges, but don't do any rank assignment yet
+		// First use the information from the greedy cycle remover to
+		// invert the leftward edges internally
+		for (var j = 0; j < conns.length; j++)
+		{
+			var cell = layout.getVisibleTerminal(conns[j], false);
+
+			// Looking for outgoing edges only
+			if (cell != vertices[i] && layout.graph.model.isVertex(cell) &&
+					!layout.isVertexIgnored(cell))
+			{
+				// We process all edge between this source and its targets
+				// If there are edges going both ways, we need to collect
+				// them all into one internal edges to avoid looping problems
+				// later. We assume this direction (source -> target) is the 
+				// natural direction if at least half the edges are going in
+				// that direction.
+
+				// The check below for edges[0] being in the vertex mapper is
+				// in case we've processed this the other way around
+				// (target -> source) and the number of edges in each direction
+				// are the same. All the graph edges will have been assigned to
+				// an internal edge going the other way, so we don't want to 
+				// process them again
+				var undirectedEdges = layout.getEdgesBetween(vertices[i],
+						cell, false);
+				var directedEdges = layout.getEdgesBetween(vertices[i],
+						cell, true);
+				
+				if (undirectedEdges != null &&
+						undirectedEdges.length > 0 &&
+						this.edgeMapper.get(undirectedEdges[0]) == null &&
+						directedEdges.length * 2 >= undirectedEdges.length)
+				{
+					var internalEdge = new mxGraphHierarchyEdge(undirectedEdges);
+
+					for (var k = 0; k < undirectedEdges.length; k++)
+					{
+						var edge = undirectedEdges[k];
+						this.edgeMapper.put(edge, internalEdge);
+
+						// Resets all point on the edge and disables the edge style
+						// without deleting it from the cell style
+						graph.resetEdge(edge);
+
+					    if (layout.disableEdgeStyle)
+					    {
+					    	layout.setEdgeStyleEnabled(edge, false);
+					    	layout.setOrthogonalEdge(edge,true);
+					    }
+					}
+
+					internalEdge.source = internalVertices[i];
+
+					if (mxUtils.indexOf(internalVertices[i].connectsAsSource, internalEdge) < 0)
+					{
+						internalVertices[i].connectsAsSource.push(internalEdge);
+					}
+				}
+			}
+		}
+
+		// Ensure temp variable is cleared from any previous use
+		internalVertices[i].temp[0] = 0;
+	}
+};
+
+/**
+ * Function: initialRank
+ *
+ * Basic determination of minimum layer ranking by working from from sources
+ * or sinks and working through each node in the relevant edge direction.
+ * Starting at the sinks is basically a longest path layering algorithm.
+*/
+mxSwimlaneModel.prototype.initialRank = function()
+{
+	this.ranksPerGroup = [];
+	
+	var startNodes = [];
+	var seen = new Object();
+
+	if (this.roots != null)
+	{
+		for (var i = 0; i < this.roots.length; i++)
+		{
+			var internalNode = this.vertexMapper.get(this.roots[i]);
+			this.maxChainDfs(null, internalNode, null, seen, 0);
+
+			if (internalNode != null)
+			{
+				startNodes.push(internalNode);
+			}
+		}
+	}
+
+	// Calculate the lower and upper rank bounds of each swimlane
+	var lowerRank = [];
+	var upperRank = [];
+	
+	for (var i = this.ranksPerGroup.length - 1; i >= 0; i--)
+	{
+		if (i == this.ranksPerGroup.length - 1)
+		{
+			lowerRank[i] = 0;
+		}
+		else
+		{
+			lowerRank[i] = upperRank[i+1] + 1;
+		}
+		
+		upperRank[i] = lowerRank[i] + this.ranksPerGroup[i];
+	}
+	
+	this.maxRank = upperRank[0];
+
+	var internalNodes = this.vertexMapper.getValues();
+	
+	for (var i=0; i < internalNodes.length; i++)
+	{
+		// Mark the node as not having had a layer assigned
+		internalNodes[i].temp[0] = -1;
+	}
+
+	var startNodesCopy = startNodes.slice();
+	
+	while (startNodes.length > 0)
+	{
+		var internalNode = startNodes[0];
+		var layerDeterminingEdges;
+		var edgesToBeMarked;
+
+		layerDeterminingEdges = internalNode.connectsAsTarget;
+		edgesToBeMarked = internalNode.connectsAsSource;
+
+		// flag to keep track of whether or not all layer determining
+		// edges have been scanned
+		var allEdgesScanned = true;
+
+		// Work out the layer of this node from the layer determining
+		// edges. The minimum layer number of any node connected by one of
+		// the layer determining edges variable
+		var minimumLayer = upperRank[0];
+
+		for (var i = 0; i < layerDeterminingEdges.length; i++)
+		{
+			var internalEdge = layerDeterminingEdges[i];
+
+			if (internalEdge.temp[0] == 5270620)
+			{
+				// This edge has been scanned, get the layer of the
+				// node on the other end
+				var otherNode = internalEdge.source;
+				minimumLayer = Math.min(minimumLayer, otherNode.temp[0] - 1);
+			}
+			else
+			{
+				allEdgesScanned = false;
+
+				break;
+			}
+		}
+
+		// If all edge have been scanned, assign the layer, mark all
+		// edges in the other direction and remove from the nodes list
+		if (allEdgesScanned)
+		{
+			if (minimumLayer > upperRank[internalNode.swimlaneIndex])
+			{
+				minimumLayer = upperRank[internalNode.swimlaneIndex];
+			}
+
+			internalNode.temp[0] = minimumLayer;
+
+			if (edgesToBeMarked != null)
+			{
+				for (var i = 0; i < edgesToBeMarked.length; i++)
+				{
+					var internalEdge = edgesToBeMarked[i];
+
+					// Assign unique stamp ( y/m/d/h )
+					internalEdge.temp[0] = 5270620;
+
+					// Add node on other end of edge to LinkedList of
+					// nodes to be analysed
+					var otherNode = internalEdge.target;
+
+					// Only add node if it hasn't been assigned a layer
+					if (otherNode.temp[0] == -1)
+					{
+						startNodes.push(otherNode);
+
+						// Mark this other node as neither being
+						// unassigned nor assigned so it isn't
+						// added to this list again, but it's
+						// layer isn't used in any calculation.
+						otherNode.temp[0] = -2;
+					}
+				}
+			}
+
+			startNodes.shift();
+		}
+		else
+		{
+			// Not all the edges have been scanned, get to the back of
+			// the class and put the dunces cap on
+			var removedCell = startNodes.shift();
+			startNodes.push(internalNode);
+
+			if (removedCell == internalNode && startNodes.length == 1)
+			{
+				// This is an error condition, we can't get out of
+				// this loop. It could happen for more than one node
+				// but that's a lot harder to detect. Log the error
+				// TODO make log comment
+				break;
+			}
+		}
+	}
+
+	// Normalize the ranks down from their large starting value to place
+	// at least 1 sink on layer 0
+//	for (var key in this.vertexMapper)
+//	{
+//		var internalNode = this.vertexMapper[key];
+//		// Mark the node as not having had a layer assigned
+//		internalNode.temp[0] -= this.maxRank;
+//	}
+	
+	// Tighten the rank 0 nodes as far as possible
+//	for ( var i = 0; i < startNodesCopy.length; i++)
+//	{
+//		var internalNode = startNodesCopy[i];
+//		var currentMaxLayer = 0;
+//		var layerDeterminingEdges = internalNode.connectsAsSource;
+//
+//		for ( var j = 0; j < layerDeterminingEdges.length; j++)
+//		{
+//			var internalEdge = layerDeterminingEdges[j];
+//			var otherNode = internalEdge.target;
+//			internalNode.temp[0] = Math.max(currentMaxLayer,
+//					otherNode.temp[0] + 1);
+//			currentMaxLayer = internalNode.temp[0];
+//		}
+//	}
+};
+
+/**
+ * Function: maxChainDfs
+ *
+ * Performs a depth first search on the internal hierarchy model. This dfs
+ * extends the default version by keeping track of chains within groups.
+ * Any cycles should be removed prior to running, but previously seen cells
+ * are ignored.
+ *
+ * Parameters:
+ *
+ * parent - the parent internal node of the current internal node
+ * root - the current internal node
+ * connectingEdge - the internal edge connecting the internal node and the parent
+ * internal node, if any
+ * seen - a set of all nodes seen by this dfs
+ * chainCount - the number of edges in the chain of vertices going through
+ * the current swimlane
+ */
+mxSwimlaneModel.prototype.maxChainDfs = function(parent, root, connectingEdge, seen, chainCount)
+{
+	if (root != null)
+	{
+		var rootId = mxCellPath.create(root.cell);
+
+		if (seen[rootId] == null)
+		{
+			seen[rootId] = root;
+			var slIndex = root.swimlaneIndex;
+			
+			if (this.ranksPerGroup[slIndex] == null || this.ranksPerGroup[slIndex] < chainCount)
+			{
+				this.ranksPerGroup[slIndex] = chainCount;
+			}
+
+			// Copy the connects as source list so that visitors
+			// can change the original for edge direction inversions
+			var outgoingEdges = root.connectsAsSource.slice();
+
+			for (var i = 0; i < outgoingEdges.length; i++)
+			{
+				var internalEdge = outgoingEdges[i];
+				var targetNode = internalEdge.target;
+
+				// Only navigate in source->target direction within the same
+				// swimlane, or from a lower index swimlane to a higher one
+				if (root.swimlaneIndex < targetNode.swimlaneIndex)
+				{
+					this.maxChainDfs(root, targetNode, internalEdge, mxUtils.clone(seen, null , true), 0);
+				}
+				else if (root.swimlaneIndex == targetNode.swimlaneIndex)
+				{
+					this.maxChainDfs(root, targetNode, internalEdge, mxUtils.clone(seen, null , true), chainCount + 1);
+				}
+			}
+		}
+	}
+};
+
+/**
+ * Function: fixRanks
+ *
+ * Fixes the layer assignments to the values stored in the nodes. Also needs
+ * to create dummy nodes for edges that cross layers.
+ */
+mxSwimlaneModel.prototype.fixRanks = function()
+{
+	var rankList = [];
+	this.ranks = [];
+
+	for (var i = 0; i < this.maxRank + 1; i++)
+	{
+		rankList[i] = [];
+		this.ranks[i] = rankList[i];
+	}
+
+	// Perform a DFS to obtain an initial ordering for each rank.
+	// Without doing this you would end up having to process
+	// crossings for a standard tree.
+	var rootsArray = null;
+
+	if (this.roots != null)
+	{
+		var oldRootsArray = this.roots;
+		rootsArray = [];
+
+		for (var i = 0; i < oldRootsArray.length; i++)
+		{
+			var cell = oldRootsArray[i];
+			var internalNode = this.vertexMapper.get(cell);
+			rootsArray[i] = internalNode;
+		}
+	}
+
+	this.visit(function(parent, node, edge, layer, seen)
+	{
+		if (seen == 0 && node.maxRank < 0 && node.minRank < 0)
+		{
+			rankList[node.temp[0]].push(node);
+			node.maxRank = node.temp[0];
+			node.minRank = node.temp[0];
+
+			// Set temp[0] to the nodes position in the rank
+			node.temp[0] = rankList[node.maxRank].length - 1;
+		}
+
+		if (parent != null && edge != null)
+		{
+			var parentToCellRankDifference = parent.maxRank - node.maxRank;
+
+			if (parentToCellRankDifference > 1)
+			{
+				// There are ranks in between the parent and current cell
+				edge.maxRank = parent.maxRank;
+				edge.minRank = node.maxRank;
+				edge.temp = [];
+				edge.x = [];
+				edge.y = [];
+
+				for (var i = edge.minRank + 1; i < edge.maxRank; i++)
+				{
+					// The connecting edge must be added to the
+					// appropriate ranks
+					rankList[i].push(edge);
+					edge.setGeneralPurposeVariable(i, rankList[i]
+							.length - 1);
+				}
+			}
+		}
+	}, rootsArray, false, null);
+};
+
+/**
+ * Function: visit
+ *
+ * A depth first search through the internal heirarchy model.
+ *
+ * Parameters:
+ *
+ * visitor - The visitor function pattern to be called for each node.
+ * trackAncestors - Whether or not the search is to keep track all nodes
+ * directly above this one in the search path.
+ */
+mxSwimlaneModel.prototype.visit = function(visitor, dfsRoots, trackAncestors, seenNodes)
+{
+	// Run dfs through on all roots
+	if (dfsRoots != null)
+	{
+		for (var i = 0; i < dfsRoots.length; i++)
+		{
+			var internalNode = dfsRoots[i];
+
+			if (internalNode != null)
+			{
+				if (seenNodes == null)
+				{
+					seenNodes = new Object();
+				}
+
+				if (trackAncestors)
+				{
+					// Set up hash code for root
+					internalNode.hashCode = [];
+					internalNode.hashCode[0] = this.dfsCount;
+					internalNode.hashCode[1] = i;
+					this.extendedDfs(null, internalNode, null, visitor, seenNodes,
+							internalNode.hashCode, i, 0);
+				}
+				else
+				{
+					this.dfs(null, internalNode, null, visitor, seenNodes, 0);
+				}
+			}
+		}
+
+		this.dfsCount++;
+	}
+};
+
+/**
+ * Function: dfs
+ *
+ * Performs a depth first search on the internal hierarchy model
+ *
+ * Parameters:
+ *
+ * parent - the parent internal node of the current internal node
+ * root - the current internal node
+ * connectingEdge - the internal edge connecting the internal node and the parent
+ * internal node, if any
+ * visitor - the visitor pattern to be called for each node
+ * seen - a set of all nodes seen by this dfs a set of all of the
+ * ancestor node of the current node
+ * layer - the layer on the dfs tree ( not the same as the model ranks )
+ */
+mxSwimlaneModel.prototype.dfs = function(parent, root, connectingEdge, visitor, seen, layer)
+{
+	if (root != null)
+	{
+		var rootId = root.id;
+
+		if (seen[rootId] == null)
+		{
+			seen[rootId] = root;
+			visitor(parent, root, connectingEdge, layer, 0);
+
+			// Copy the connects as source list so that visitors
+			// can change the original for edge direction inversions
+			var outgoingEdges = root.connectsAsSource.slice();
+			
+			for (var i = 0; i< outgoingEdges.length; i++)
+			{
+				var internalEdge = outgoingEdges[i];
+				var targetNode = internalEdge.target;
+
+				// Root check is O(|roots|)
+				this.dfs(root, targetNode, internalEdge, visitor, seen,
+						layer + 1);
+			}
+		}
+		else
+		{
+			// Use the int field to indicate this node has been seen
+			visitor(parent, root, connectingEdge, layer, 1);
+		}
+	}
+};
+
+/**
+ * Function: extendedDfs
+ *
+ * Performs a depth first search on the internal hierarchy model. This dfs
+ * extends the default version by keeping track of cells ancestors, but it
+ * should be only used when necessary because of it can be computationally
+ * intensive for deep searches.
+ *
+ * Parameters:
+ *
+ * parent - the parent internal node of the current internal node
+ * root - the current internal node
+ * connectingEdge - the internal edge connecting the internal node and the parent
+ * internal node, if any
+ * visitor - the visitor pattern to be called for each node
+ * seen - a set of all nodes seen by this dfs
+ * ancestors - the parent hash code
+ * childHash - the new hash code for this node
+ * layer - the layer on the dfs tree ( not the same as the model ranks )
+ */
+mxSwimlaneModel.prototype.extendedDfs = function(parent, root, connectingEdge, visitor, seen, ancestors, childHash, layer)
+{
+	// Explanation of custom hash set. Previously, the ancestors variable
+	// was passed through the dfs as a HashSet. The ancestors were copied
+	// into a new HashSet and when the new child was processed it was also
+	// added to the set. If the current node was in its ancestor list it
+	// meant there is a cycle in the graph and this information is passed
+	// to the visitor.visit() in the seen parameter. The HashSet clone was
+	// very expensive on CPU so a custom hash was developed using primitive
+	// types. temp[] couldn't be used so hashCode[] was added to each node.
+	// Each new child adds another int to the array, copying the prefix
+	// from its parent. Child of the same parent add different ints (the
+	// limit is therefore 2^32 children per parent...). If a node has a
+	// child with the hashCode already set then the child code is compared
+	// to the same portion of the current nodes array. If they match there
+	// is a loop.
+	// Note that the basic mechanism would only allow for 1 use of this
+	// functionality, so the root nodes have two ints. The second int is
+	// incremented through each node root and the first is incremented
+	// through each run of the dfs algorithm (therefore the dfs is not
+	// thread safe). The hash code of each node is set if not already set,
+	// or if the first int does not match that of the current run.
+	if (root != null)
+	{
+		if (parent != null)
+		{
+			// Form this nodes hash code if necessary, that is, if the
+			// hashCode variable has not been initialized or if the
+			// start of the parent hash code does not equal the start of
+			// this nodes hash code, indicating the code was set on a
+			// previous run of this dfs.
+			if (root.hashCode == null ||
+				root.hashCode[0] != parent.hashCode[0])
+			{
+				var hashCodeLength = parent.hashCode.length + 1;
+				root.hashCode = parent.hashCode.slice();
+				root.hashCode[hashCodeLength - 1] = childHash;
+			}
+		}
+
+		var rootId = root.id;
+
+		if (seen[rootId] == null)
+		{
+			seen[rootId] = root;
+			visitor(parent, root, connectingEdge, layer, 0);
+
+			// Copy the connects as source list so that visitors
+			// can change the original for edge direction inversions
+			var outgoingEdges = root.connectsAsSource.slice();
+			var incomingEdges = root.connectsAsTarget.slice();
+
+			for (var i = 0; i < outgoingEdges.length; i++)
+			{
+				var internalEdge = outgoingEdges[i];
+				var targetNode = internalEdge.target;
+				
+				// Only navigate in source->target direction within the same
+				// swimlane, or from a lower index swimlane to a higher one
+				if (root.swimlaneIndex <= targetNode.swimlaneIndex)
+				{
+					this.extendedDfs(root, targetNode, internalEdge, visitor, seen,
+							root.hashCode, i, layer + 1);
+				}
+			}
+			
+			for (var i = 0; i < incomingEdges.length; i++)
+			{
+				var internalEdge = incomingEdges[i];
+				var targetNode = internalEdge.source;
+
+				// Only navigate in target->source direction from a lower index 
+				// swimlane to a higher one
+				if (root.swimlaneIndex < targetNode.swimlaneIndex)
+				{
+					this.extendedDfs(root, targetNode, internalEdge, visitor, seen,
+							root.hashCode, i, layer + 1);
+				}
+			}
+		}
+		else
+		{
+			// Use the int field to indicate this node has been seen
+			visitor(parent, root, connectingEdge, layer, 1);
+		}
+	}
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/mxHierarchicalLayout.js b/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/mxHierarchicalLayout.js
new file mode 100644
index 0000000..deb60b5
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/mxHierarchicalLayout.js
@@ -0,0 +1,846 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxHierarchicalLayout
+ * 
+ * A hierarchical layout algorithm.
+ * 
+ * Constructor: mxHierarchicalLayout
+ *
+ * Constructs a new hierarchical layout algorithm.
+ *
+ * Arguments:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * orientation - Optional constant that defines the orientation of this
+ * layout.
+ * deterministic - Optional boolean that specifies if this layout should be
+ * deterministic. Default is true.
+ */
+function mxHierarchicalLayout(graph, orientation, deterministic)
+{
+	mxGraphLayout.call(this, graph);
+	this.orientation = (orientation != null) ? orientation : mxConstants.DIRECTION_NORTH;
+	this.deterministic = (deterministic != null) ? deterministic : true;
+};
+
+var mxHierarchicalEdgeStyle =
+{
+	ORTHOGONAL: 1,
+	POLYLINE: 2,
+	STRAIGHT: 3,
+	CURVE: 4
+};
+
+/**
+ * Extends mxGraphLayout.
+ */
+mxHierarchicalLayout.prototype = new mxGraphLayout();
+mxHierarchicalLayout.prototype.constructor = mxHierarchicalLayout;
+
+/**
+ * Variable: roots
+ * 
+ * Holds the array of <mxCell> that this layout contains.
+ */
+mxHierarchicalLayout.prototype.roots = null;
+
+/**
+ * Variable: resizeParent
+ * 
+ * Specifies if the parent should be resized after the layout so that it
+ * contains all the child cells. Default is false. See also <parentBorder>.
+ */
+mxHierarchicalLayout.prototype.resizeParent = false;
+
+/**
+ * Variable: maintainParentLocation
+ * 
+ * Specifies if the parent location should be maintained, so that the
+ * top, left corner stays the same before and after execution of
+ * the layout. Default is false for backwards compatibility.
+ */
+mxHierarchicalLayout.prototype.maintainParentLocation = false;
+
+/**
+ * Variable: moveParent
+ * 
+ * Specifies if the parent should be moved if <resizeParent> is enabled.
+ * Default is false.
+ */
+mxHierarchicalLayout.prototype.moveParent = false;
+
+/**
+ * Variable: parentBorder
+ * 
+ * The border to be added around the children if the parent is to be
+ * resized using <resizeParent>. Default is 0.
+ */
+mxHierarchicalLayout.prototype.parentBorder = 0;
+
+/**
+ * Variable: intraCellSpacing
+ * 
+ * The spacing buffer added between cells on the same layer. Default is 30.
+ */
+mxHierarchicalLayout.prototype.intraCellSpacing = 30;
+
+/**
+ * Variable: interRankCellSpacing
+ * 
+ * The spacing buffer added between cell on adjacent layers. Default is 50.
+ */
+mxHierarchicalLayout.prototype.interRankCellSpacing = 100;
+
+/**
+ * Variable: interHierarchySpacing
+ * 
+ * The spacing buffer between unconnected hierarchies. Default is 60.
+ */
+mxHierarchicalLayout.prototype.interHierarchySpacing = 60;
+
+/**
+ * Variable: parallelEdgeSpacing
+ * 
+ * The distance between each parallel edge on each ranks for long edges
+ */
+mxHierarchicalLayout.prototype.parallelEdgeSpacing = 10;
+
+/**
+ * Variable: orientation
+ * 
+ * The position of the root node(s) relative to the laid out graph in.
+ * Default is <mxConstants.DIRECTION_NORTH>.
+ */
+mxHierarchicalLayout.prototype.orientation = mxConstants.DIRECTION_NORTH;
+
+/**
+ * Variable: fineTuning
+ * 
+ * Whether or not to perform local optimisations and iterate multiple times
+ * through the algorithm. Default is true.
+ */
+mxHierarchicalLayout.prototype.fineTuning = true;
+
+/**
+ * 
+ * Variable: tightenToSource
+ * 
+ * Whether or not to tighten the assigned ranks of vertices up towards
+ * the source cells.
+ */
+mxHierarchicalLayout.prototype.tightenToSource = true;
+
+/**
+ * Variable: disableEdgeStyle
+ * 
+ * Specifies if the STYLE_NOEDGESTYLE flag should be set on edges that are
+ * modified by the result. Default is true.
+ */
+mxHierarchicalLayout.prototype.disableEdgeStyle = true;
+
+/**
+ * Variable: traverseAncestors
+ * 
+ * Whether or not to drill into child cells and layout in reverse
+ * group order. This also cause the layout to navigate edges whose 
+ * terminal vertices  * have different parents but are in the same 
+ * ancestry chain
+ */
+mxHierarchicalLayout.prototype.traverseAncestors = true;
+
+/**
+ * Variable: model
+ * 
+ * The internal <mxGraphHierarchyModel> formed of the layout.
+ */
+mxHierarchicalLayout.prototype.model = null;
+
+/**
+ * Variable: edgesSet
+ * 
+ * A cache of edges whose source terminal is the key
+ */
+mxHierarchicalLayout.prototype.edgesCache = null;
+
+/**
+ * Variable: edgesSet
+ * 
+ * A cache of edges whose source terminal is the key
+ */
+mxHierarchicalLayout.prototype.edgeSourceTermCache = null;
+
+/**
+ * Variable: edgesSet
+ * 
+ * A cache of edges whose source terminal is the key
+ */
+mxHierarchicalLayout.prototype.edgesTargetTermCache = null;
+
+/**
+ * Variable: edgeStyle
+ * 
+ * The style to apply between cell layers to edge segments
+ */
+mxHierarchicalLayout.prototype.edgeStyle = mxHierarchicalEdgeStyle.POLYLINE;
+
+/**
+ * Function: getModel
+ * 
+ * Returns the internal <mxGraphHierarchyModel> for this layout algorithm.
+ */
+mxHierarchicalLayout.prototype.getModel = function()
+{
+	return this.model;
+};
+
+/**
+ * Function: execute
+ * 
+ * Executes the layout for the children of the specified parent.
+ * 
+ * Parameters:
+ * 
+ * parent - Parent <mxCell> that contains the children to be laid out.
+ * roots - Optional starting roots of the layout.
+ */
+mxHierarchicalLayout.prototype.execute = function(parent, roots)
+{
+	this.parent = parent;
+	var model = this.graph.model;
+	this.edgesCache = new mxDictionary();
+	this.edgeSourceTermCache = new mxDictionary();
+	this.edgesTargetTermCache = new mxDictionary();
+
+	if (roots != null && !(roots instanceof Array))
+	{
+		roots = [roots];
+	}
+	
+	// If the roots are set and the parent is set, only
+	// use the roots that are some dependent of the that
+	// parent.
+	// If just the root are set, use them as-is
+	// If just the parent is set use it's immediate
+	// children as the initial set
+
+	if (roots == null && parent == null)
+	{
+		// TODO indicate the problem
+		return;
+	}
+	
+	//  Maintaining parent location
+	this.parentX = null;
+	this.parentY = null;
+	
+	if (parent != this.root && model.isVertex(parent) != null && this.maintainParentLocation)
+	{
+		var geo = this.graph.getCellGeometry(parent);
+		
+		if (geo != null)
+		{
+			this.parentX = geo.x;
+			this.parentY = geo.y;
+		}
+	}
+	
+	if (roots != null)
+	{
+		var rootsCopy = [];
+
+		for (var i = 0; i < roots.length; i++)
+		{
+			var ancestor = parent != null ? model.isAncestor(parent, roots[i]) : true;
+			
+			if (ancestor && model.isVertex(roots[i]))
+			{
+				rootsCopy.push(roots[i]);
+			}
+		}
+
+		this.roots = rootsCopy;
+	}
+	
+	model.beginUpdate();
+	try
+	{
+		this.run(parent);
+		
+		if (this.resizeParent && !this.graph.isCellCollapsed(parent))
+		{
+			this.graph.updateGroupBounds([parent], this.parentBorder, this.moveParent);
+		}
+		
+		// Maintaining parent location
+		if (this.parentX != null && this.parentY != null)
+		{
+			var geo = this.graph.getCellGeometry(parent);
+			
+			if (geo != null)
+			{
+				geo = geo.clone();
+				geo.x = this.parentX;
+				geo.y = this.parentY;
+				model.setGeometry(parent, geo);
+			}
+		}
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+};
+
+/**
+ * Function: findRoots
+ * 
+ * Returns all visible children in the given parent which do not have
+ * incoming edges. If the result is empty then the children with the
+ * maximum difference between incoming and outgoing edges are returned.
+ * This takes into account edges that are being promoted to the given
+ * root due to invisible children or collapsed cells.
+ * 
+ * Parameters:
+ * 
+ * parent - <mxCell> whose children should be checked.
+ * vertices - array of vertices to limit search to
+ */
+mxHierarchicalLayout.prototype.findRoots = function(parent, vertices)
+{
+	var roots = [];
+	
+	if (parent != null && vertices != null)
+	{
+		var model = this.graph.model;
+		var best = null;
+		var maxDiff = -100000;
+		
+		for (var i in vertices)
+		{
+			var cell = vertices[i];
+
+			if (model.isVertex(cell) && this.graph.isCellVisible(cell))
+			{
+				var conns = this.getEdges(cell);
+				var fanOut = 0;
+				var fanIn = 0;
+
+				for (var k = 0; k < conns.length; k++)
+				{
+					var src = this.getVisibleTerminal(conns[k], true);
+
+					if (src == cell)
+					{
+						fanOut++;
+					}
+					else
+					{
+						fanIn++;
+					}
+				}
+
+				if (fanIn == 0 && fanOut > 0)
+				{
+					roots.push(cell);
+				}
+
+				var diff = fanOut - fanIn;
+
+				if (diff > maxDiff)
+				{
+					maxDiff = diff;
+					best = cell;
+				}
+			}
+		}
+		
+		if (roots.length == 0 && best != null)
+		{
+			roots.push(best);
+		}
+	}
+	
+	return roots;
+};
+
+/**
+ * Function: getEdges
+ * 
+ * Returns the connected edges for the given cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose edges should be returned.
+ */
+mxHierarchicalLayout.prototype.getEdges = function(cell)
+{
+	var cachedEdges = this.edgesCache.get(cell);
+	
+	if (cachedEdges != null)
+	{
+		return cachedEdges;
+	}
+
+	var model = this.graph.model;
+	var edges = [];
+	var isCollapsed = this.graph.isCellCollapsed(cell);
+	var childCount = model.getChildCount(cell);
+
+	for (var i = 0; i < childCount; i++)
+	{
+		var child = model.getChildAt(cell, i);
+
+		if (this.isPort(child))
+		{
+			edges = edges.concat(model.getEdges(child, true, true));
+		}
+		else if (isCollapsed || !this.graph.isCellVisible(child))
+		{
+			edges = edges.concat(model.getEdges(child, true, true));
+		}
+	}
+
+	edges = edges.concat(model.getEdges(cell, true, true));
+	var result = [];
+	
+	for (var i = 0; i < edges.length; i++)
+	{
+		var source = this.getVisibleTerminal(edges[i], true);
+		var target = this.getVisibleTerminal(edges[i], false);
+		
+		if ((source == target) || ((source != target) && ((target == cell && (this.parent == null || this.graph.isValidAncestor(source, this.parent, this.traverseAncestors))) ||
+			(source == cell && (this.parent == null ||
+					this.graph.isValidAncestor(target, this.parent, this.traverseAncestors))))))
+		{
+			result.push(edges[i]);
+		}
+	}
+
+	this.edgesCache.put(cell, result);
+
+	return result;
+};
+
+/**
+ * Function: getVisibleTerminal
+ * 
+ * Helper function to return visible terminal for edge allowing for ports
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> whose edges should be returned.
+ * source - Boolean that specifies whether the source or target terminal is to be returned
+ */
+mxHierarchicalLayout.prototype.getVisibleTerminal = function(edge, source)
+{
+	var terminalCache = this.edgesTargetTermCache;
+	
+	if (source)
+	{
+		terminalCache = this.edgeSourceTermCache;
+	}
+
+	var term = terminalCache.get(edge);
+
+	if (term != null)
+	{
+		return term;
+	}
+
+	var state = this.graph.view.getState(edge);
+	
+	var terminal = (state != null) ? state.getVisibleTerminal(source) : this.graph.view.getVisibleTerminal(edge, source);
+	
+	if (terminal == null)
+	{
+		terminal = (state != null) ? state.getVisibleTerminal(source) : this.graph.view.getVisibleTerminal(edge, source);
+	}
+
+	if (terminal != null)
+	{
+		if (this.isPort(terminal))
+		{
+			terminal = this.graph.model.getParent(terminal);
+		}
+		
+		terminalCache.put(edge, terminal);
+	}
+
+	return terminal;
+};
+
+/**
+ * Function: run
+ * 
+ * The API method used to exercise the layout upon the graph description
+ * and produce a separate description of the vertex position and edge
+ * routing changes made. It runs each stage of the layout that has been
+ * created.
+ */
+mxHierarchicalLayout.prototype.run = function(parent)
+{
+	// Separate out unconnected hierarchies
+	var hierarchyVertices = [];
+	var allVertexSet = [];
+
+	if (this.roots == null && parent != null)
+	{
+		var filledVertexSet = Object();
+		this.filterDescendants(parent, filledVertexSet);
+
+		this.roots = [];
+		var filledVertexSetEmpty = true;
+
+		// Poor man's isSetEmpty
+		for (var key in filledVertexSet)
+		{
+			if (filledVertexSet[key] != null)
+			{
+				filledVertexSetEmpty = false;
+				break;
+			}
+		}
+
+		while (!filledVertexSetEmpty)
+		{
+			var candidateRoots = this.findRoots(parent, filledVertexSet);
+			
+			// If the candidate root is an unconnected group cell, remove it from
+			// the layout. We may need a custom set that holds such groups and forces
+			// them to be processed for resizing and/or moving.
+			
+
+			for (var i = 0; i < candidateRoots.length; i++)
+			{
+				var vertexSet = Object();
+				hierarchyVertices.push(vertexSet);
+
+				this.traverse(candidateRoots[i], true, null, allVertexSet, vertexSet,
+						hierarchyVertices, filledVertexSet);
+			}
+
+			for (var i = 0; i < candidateRoots.length; i++)
+			{
+				this.roots.push(candidateRoots[i]);
+			}
+			
+			filledVertexSetEmpty = true;
+			
+			// Poor man's isSetEmpty
+			for (var key in filledVertexSet)
+			{
+				if (filledVertexSet[key] != null)
+				{
+					filledVertexSetEmpty = false;
+					break;
+				}
+			}
+		}
+	}
+	else
+	{
+		// Find vertex set as directed traversal from roots
+
+		for (var i = 0; i < this.roots.length; i++)
+		{
+			var vertexSet = Object();
+			hierarchyVertices.push(vertexSet);
+
+			this.traverse(this.roots[i], true, null, allVertexSet, vertexSet,
+					hierarchyVertices, null);
+		}
+	}
+
+	// Iterate through the result removing parents who have children in this layout
+	
+	// Perform a layout for each seperate hierarchy
+	// Track initial coordinate x-positioning
+	var initialX = 0;
+
+	for (var i = 0; i < hierarchyVertices.length; i++)
+	{
+		var vertexSet = hierarchyVertices[i];
+		var tmp = [];
+		
+		for (var key in vertexSet)
+		{
+			tmp.push(vertexSet[key]);
+		}
+		
+		this.model = new mxGraphHierarchyModel(this, tmp, this.roots,
+			parent, this.tightenToSource);
+
+		this.cycleStage(parent);
+		this.layeringStage();
+		
+		this.crossingStage(parent);
+		initialX = this.placementStage(initialX, parent);
+	}
+};
+
+/**
+ * Function: filterDescendants
+ * 
+ * Creates an array of descendant cells
+ */
+mxHierarchicalLayout.prototype.filterDescendants = function(cell, result)
+{
+	var model = this.graph.model;
+
+	if (model.isVertex(cell) && cell != this.parent && this.graph.isCellVisible(cell))
+	{
+		result[mxObjectIdentity.get(cell)] = cell;
+	}
+
+	if (this.traverseAncestors || cell == this.parent
+			&& this.graph.isCellVisible(cell))
+	{
+		var childCount = model.getChildCount(cell);
+
+		for (var i = 0; i < childCount; i++)
+		{
+			var child = model.getChildAt(cell, i);
+			
+			// Ignore ports in the layout vertex list, they are dealt with
+			// in the traversal mechanisms
+			if (!this.isPort(child))
+			{
+				this.filterDescendants(child, result);
+			}
+		}
+	}
+};
+
+/**
+ * Function: isPort
+ * 
+ * Returns true if the given cell is a "port", that is, when connecting to
+ * it, its parent is the connecting vertex in terms of graph traversal
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the port.
+ */
+mxHierarchicalLayout.prototype.isPort = function(cell)
+{
+	if (cell.geometry.relative)
+	{
+		return true;
+	}
+	
+	return false;
+};
+
+/**
+ * Function: getEdgesBetween
+ * 
+ * Returns the edges between the given source and target. This takes into
+ * account collapsed and invisible cells and ports.
+ * 
+ * Parameters:
+ * 
+ * source -
+ * target -
+ * directed -
+ */
+mxHierarchicalLayout.prototype.getEdgesBetween = function(source, target, directed)
+{
+	directed = (directed != null) ? directed : false;
+	var edges = this.getEdges(source);
+	var result = [];
+
+	// Checks if the edge is connected to the correct
+	// cell and returns the first match
+	for (var i = 0; i < edges.length; i++)
+	{
+		var src = this.getVisibleTerminal(edges[i], true);
+		var trg = this.getVisibleTerminal(edges[i], false);
+
+		if ((src == source && trg == target) || (!directed && src == target && trg == source))
+		{
+			result.push(edges[i]);
+		}
+	}
+
+	return result;
+};
+
+/**
+ * Traverses the (directed) graph invoking the given function for each
+ * visited vertex and edge. The function is invoked with the current vertex
+ * and the incoming edge as a parameter. This implementation makes sure
+ * each vertex is only visited once. The function may return false if the
+ * traversal should stop at the given vertex.
+ * 
+ * Parameters:
+ * 
+ * vertex - <mxCell> that represents the vertex where the traversal starts.
+ * directed - boolean indicating if edges should only be traversed
+ * from source to target. Default is true.
+ * edge - Optional <mxCell> that represents the incoming edge. This is
+ * null for the first step of the traversal.
+ * allVertices - Array of cell paths for the visited cells.
+ */
+mxHierarchicalLayout.prototype.traverse = function(vertex, directed, edge, allVertices, currentComp,
+											hierarchyVertices, filledVertexSet)
+{
+	if (vertex != null && allVertices != null)
+	{
+		// Has this vertex been seen before in any traversal
+		// And if the filled vertex set is populated, only 
+		// process vertices in that it contains
+		var vertexID = mxObjectIdentity.get(vertex);
+		
+		if ((allVertices[vertexID] == null)
+				&& (filledVertexSet == null ? true : filledVertexSet[vertexID] != null))
+		{
+			if (currentComp[vertexID] == null)
+			{
+				currentComp[vertexID] = vertex;
+			}
+			if (allVertices[vertexID] == null)
+			{
+				allVertices[vertexID] = vertex;
+			}
+
+			if (filledVertexSet !== null)
+			{
+				delete filledVertexSet[vertexID];
+			}
+
+			var edges = this.getEdges(vertex);
+			var edgeIsSource = [];
+
+			for (var i = 0; i < edges.length; i++)
+			{
+				edgeIsSource[i] = (this.getVisibleTerminal(edges[i], true) == vertex);
+			}
+
+			for (var i = 0; i < edges.length; i++)
+			{
+				if (!directed || edgeIsSource[i])
+				{
+					var next = this.getVisibleTerminal(edges[i], !edgeIsSource[i]);
+					
+					// Check whether there are more edges incoming from the target vertex than outgoing
+					// The hierarchical model treats bi-directional parallel edges as being sourced
+					// from the more "sourced" terminal. If the directions are equal in number, the direction
+					// is that of the natural direction from the roots of the layout.
+					// The checks below are slightly more verbose than need be for performance reasons
+					var netCount = 1;
+
+					for (var j = 0; j < edges.length; j++)
+					{
+						if (j == i)
+						{
+							continue;
+						}
+						else
+						{
+							var isSource2 = edgeIsSource[j];
+							var otherTerm = this.getVisibleTerminal(edges[j], !isSource2);
+							
+							if (otherTerm == next)
+							{
+								if (isSource2)
+								{
+									netCount++;
+								}
+								else
+								{
+									netCount--;
+								}
+							}
+						}
+					}
+
+					if (netCount >= 0)
+					{
+						currentComp = this.traverse(next, directed, edges[i], allVertices,
+							currentComp, hierarchyVertices,
+							filledVertexSet);
+					}
+				}
+			}
+		}
+		else
+		{
+			if (currentComp[vertexID] == null)
+			{
+				// We've seen this vertex before, but not in the current component
+				// This component and the one it's in need to be merged
+
+				for (var i = 0; i < hierarchyVertices.length; i++)
+				{
+					var comp = hierarchyVertices[i];
+
+					if (comp[vertexID] != null)
+					{
+						for (var key in comp)
+						{
+							currentComp[key] = comp[key];
+						}
+						
+						// Remove the current component from the hierarchy set
+						hierarchyVertices.splice(i, 1);
+						return currentComp;
+					}
+				}
+			}
+		}
+	}
+	
+	return currentComp;
+};
+
+/**
+ * Function: cycleStage
+ * 
+ * Executes the cycle stage using mxMinimumCycleRemover.
+ */
+mxHierarchicalLayout.prototype.cycleStage = function(parent)
+{
+	var cycleStage = new mxMinimumCycleRemover(this);
+	cycleStage.execute(parent);
+};
+
+/**
+ * Function: layeringStage
+ * 
+ * Implements first stage of a Sugiyama layout.
+ */
+mxHierarchicalLayout.prototype.layeringStage = function()
+{
+	this.model.initialRank();
+	this.model.fixRanks();
+};
+
+/**
+ * Function: crossingStage
+ * 
+ * Executes the crossing stage using mxMedianHybridCrossingReduction.
+ */
+mxHierarchicalLayout.prototype.crossingStage = function(parent)
+{
+	var crossingStage = new mxMedianHybridCrossingReduction(this);
+	crossingStage.execute(parent);
+};
+
+/**
+ * Function: placementStage
+ * 
+ * Executes the placement stage using mxCoordinateAssignment.
+ */
+mxHierarchicalLayout.prototype.placementStage = function(initialX, parent)
+{
+	var placementStage = new mxCoordinateAssignment(this, this.intraCellSpacing,
+			this.interRankCellSpacing, this.orientation, initialX,
+			this.parallelEdgeSpacing);
+	placementStage.fineTuning = this.fineTuning;
+	placementStage.execute(parent);
+	
+	return placementStage.limitX + this.interHierarchySpacing;
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/mxSwimlaneLayout.js b/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/mxSwimlaneLayout.js
new file mode 100644
index 0000000..669beef
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/mxSwimlaneLayout.js
@@ -0,0 +1,937 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxSwimlaneLayout
+ * 
+ * A hierarchical layout algorithm.
+ * 
+ * Constructor: mxSwimlaneLayout
+ *
+ * Constructs a new hierarchical layout algorithm.
+ *
+ * Arguments:
+ * 
+ * graph - Reference to the enclosing <mxGraph>.
+ * orientation - Optional constant that defines the orientation of this
+ * layout.
+ * deterministic - Optional boolean that specifies if this layout should be
+ * deterministic. Default is true.
+ */
+function mxSwimlaneLayout(graph, orientation, deterministic)
+{
+	mxGraphLayout.call(this, graph);
+	this.orientation = (orientation != null) ? orientation : mxConstants.DIRECTION_NORTH;
+	this.deterministic = (deterministic != null) ? deterministic : true;
+};
+
+/**
+ * Extends mxGraphLayout.
+ */
+mxSwimlaneLayout.prototype = new mxGraphLayout();
+mxSwimlaneLayout.prototype.constructor = mxSwimlaneLayout;
+
+/**
+ * Variable: roots
+ * 
+ * Holds the array of <mxCell> that this layout contains.
+ */
+mxSwimlaneLayout.prototype.roots = null;
+
+/**
+ * Variable: swimlanes
+ * 
+ * Holds the array of <mxCell> of the ordered swimlanes to lay out
+ */
+mxSwimlaneLayout.prototype.swimlanes = null;
+
+/**
+ * Variable: dummyVertices
+ * 
+ * Holds an array of <mxCell> of dummy vertices inserted during the layout
+ * to pad out empty swimlanes
+ */
+mxSwimlaneLayout.prototype.dummyVertices = null;
+
+/**
+ * Variable: dummyVertexWidth
+ * 
+ * The cell width of any dummy vertices inserted
+ */
+mxSwimlaneLayout.prototype.dummyVertexWidth = 50;
+
+/**
+ * Variable: resizeParent
+ * 
+ * Specifies if the parent should be resized after the layout so that it
+ * contains all the child cells. Default is false. See also <parentBorder>.
+ */
+mxSwimlaneLayout.prototype.resizeParent = false;
+
+/**
+ * Variable: maintainParentLocation
+ * 
+ * Specifies if the parent location should be maintained, so that the
+ * top, left corner stays the same before and after execution of
+ * the layout. Default is false for backwards compatibility.
+ */
+mxSwimlaneLayout.prototype.maintainParentLocation = false;
+
+/**
+ * Variable: moveParent
+ * 
+ * Specifies if the parent should be moved if <resizeParent> is enabled.
+ * Default is false.
+ */
+mxSwimlaneLayout.prototype.moveParent = false;
+
+/**
+ * Variable: parentBorder
+ * 
+ * The border to be added around the children if the parent is to be
+ * resized using <resizeParent>. Default is 0.
+ */
+mxSwimlaneLayout.prototype.parentBorder = 30;
+
+/**
+ * Variable: intraCellSpacing
+ * 
+ * The spacing buffer added between cells on the same layer. Default is 30.
+ */
+mxSwimlaneLayout.prototype.intraCellSpacing = 30;
+
+/**
+ * Variable: interRankCellSpacing
+ * 
+ * The spacing buffer added between cell on adjacent layers. Default is 50.
+ */
+mxSwimlaneLayout.prototype.interRankCellSpacing = 100;
+
+/**
+ * Variable: interHierarchySpacing
+ * 
+ * The spacing buffer between unconnected hierarchies. Default is 60.
+ */
+mxSwimlaneLayout.prototype.interHierarchySpacing = 60;
+
+/**
+ * Variable: parallelEdgeSpacing
+ * 
+ * The distance between each parallel edge on each ranks for long edges
+ */
+mxSwimlaneLayout.prototype.parallelEdgeSpacing = 10;
+
+/**
+ * Variable: orientation
+ * 
+ * The position of the root node(s) relative to the laid out graph in.
+ * Default is <mxConstants.DIRECTION_NORTH>.
+ */
+mxSwimlaneLayout.prototype.orientation = mxConstants.DIRECTION_NORTH;
+
+/**
+ * Variable: fineTuning
+ * 
+ * Whether or not to perform local optimisations and iterate multiple times
+ * through the algorithm. Default is true.
+ */
+mxSwimlaneLayout.prototype.fineTuning = true;
+
+/**
+ * 
+ * Variable: tightenToSource
+ * 
+ * Whether or not to tighten the assigned ranks of vertices up towards
+ * the source cells.
+ */
+mxSwimlaneLayout.prototype.tightenToSource = true;
+
+/**
+ * Variable: disableEdgeStyle
+ * 
+ * Specifies if the STYLE_NOEDGESTYLE flag should be set on edges that are
+ * modified by the result. Default is true.
+ */
+mxSwimlaneLayout.prototype.disableEdgeStyle = true;
+
+/**
+ * Variable: traverseAncestors
+ * 
+ * Whether or not to drill into child cells and layout in reverse
+ * group order. This also cause the layout to navigate edges whose 
+ * terminal vertices  * have different parents but are in the same 
+ * ancestry chain
+ */
+mxSwimlaneLayout.prototype.traverseAncestors = true;
+
+/**
+ * Variable: model
+ * 
+ * The internal <mxSwimlaneModel> formed of the layout.
+ */
+mxSwimlaneLayout.prototype.model = null;
+
+/**
+ * Variable: edgesSet
+ * 
+ * A cache of edges whose source terminal is the key
+ */
+mxSwimlaneLayout.prototype.edgesCache = null;
+
+/**
+ * Variable: edgesSet
+ * 
+ * A cache of edges whose source terminal is the key
+ */
+mxHierarchicalLayout.prototype.edgeSourceTermCache = null;
+
+/**
+ * Variable: edgesSet
+ * 
+ * A cache of edges whose source terminal is the key
+ */
+mxHierarchicalLayout.prototype.edgesTargetTermCache = null;
+
+/**
+ * Variable: edgeStyle
+ * 
+ * The style to apply between cell layers to edge segments
+ */
+mxHierarchicalLayout.prototype.edgeStyle = mxHierarchicalEdgeStyle.POLYLINE;
+
+/**
+ * Function: getModel
+ * 
+ * Returns the internal <mxSwimlaneModel> for this layout algorithm.
+ */
+mxSwimlaneLayout.prototype.getModel = function()
+{
+	return this.model;
+};
+
+/**
+ * Function: execute
+ * 
+ * Executes the layout for the children of the specified parent.
+ * 
+ * Parameters:
+ * 
+ * parent - Parent <mxCell> that contains the children to be laid out.
+ * swimlanes - Ordered array of swimlanes to be laid out
+ */
+mxSwimlaneLayout.prototype.execute = function(parent, swimlanes)
+{
+	this.parent = parent;
+	var model = this.graph.model;
+	this.edgesCache = new mxDictionary();
+	this.edgeSourceTermCache = new mxDictionary();
+	this.edgesTargetTermCache = new mxDictionary();
+
+	// If the roots are set and the parent is set, only
+	// use the roots that are some dependent of the that
+	// parent.
+	// If just the root are set, use them as-is
+	// If just the parent is set use it's immediate
+	// children as the initial set
+
+	if (swimlanes == null || swimlanes.length < 1)
+	{
+		// TODO indicate the problem
+		return;
+	}
+
+	if (parent == null)
+	{
+		parent = model.getParent(swimlanes[0]);
+	}
+
+	//  Maintaining parent location
+	this.parentX = null;
+	this.parentY = null;
+	
+	if (parent != this.root && model.isVertex(parent) != null && this.maintainParentLocation)
+	{
+		var geo = this.graph.getCellGeometry(parent);
+		
+		if (geo != null)
+		{
+			this.parentX = geo.x;
+			this.parentY = geo.y;
+		}
+	}
+
+	this.swimlanes = swimlanes;
+	this.dummyVertices = [];
+	// Check the swimlanes all have vertices
+	// in them
+	for (var i = 0; i < swimlanes.length; i++)
+	{
+		var children = this.graph.getChildCells(swimlanes[i]);
+		
+		if (children == null || children.length == 0)
+		{
+			var vertex = this.graph.insertVertex(swimlanes[i], null, null, 0, 0, this.dummyVertexWidth, 0);
+			this.dummyVertices.push(vertex);
+		}
+	}
+	
+	model.beginUpdate();
+	try
+	{
+		this.run(parent);
+		
+		if (this.resizeParent && !this.graph.isCellCollapsed(parent))
+		{
+			this.graph.updateGroupBounds([parent], this.parentBorder, this.moveParent);
+		}
+		
+		// Maintaining parent location
+		if (this.parentX != null && this.parentY != null)
+		{
+			var geo = this.graph.getCellGeometry(parent);
+			
+			if (geo != null)
+			{
+				geo = geo.clone();
+				geo.x = this.parentX;
+				geo.y = this.parentY;
+				model.setGeometry(parent, geo);
+			}
+		}
+
+		this.graph.removeCells(this.dummyVertices);
+	}
+	finally
+	{
+		model.endUpdate();
+	}
+};
+
+/**
+ * Function: updateGroupBounds
+ * 
+ * Updates the bounds of the given array of groups so that it includes
+ * all child vertices.
+ * 
+ */
+mxSwimlaneLayout.prototype.updateGroupBounds = function()
+{
+	// Get all vertices and edge in the layout
+	var cells = [];
+	var model = this.model;
+	
+	for (var key in model.edgeMapper)
+	{
+		var edge = model.edgeMapper[key];
+		
+		for (var i = 0; i < edge.edges.length; i++)
+		{
+			cells.push(edge.edges[i]);
+		}
+	}
+	
+	var layoutBounds = this.graph.getBoundingBoxFromGeometry(cells, true);
+	var childBounds = [];
+
+	for (var i = 0; i < this.swimlanes.length; i++)
+	{
+		var lane = this.swimlanes[i];
+		var geo = this.graph.getCellGeometry(lane);
+		
+		if (geo != null)
+		{
+			var children = this.graph.getChildCells(lane);
+			
+			var size = (this.graph.isSwimlane(lane)) ?
+					this.graph.getStartSize(lane) : new mxRectangle();
+
+			var bounds = this.graph.getBoundingBoxFromGeometry(children);
+			childBounds[i] = bounds;
+			var childrenY = bounds.y + geo.y - size.height - this.parentBorder;
+			var maxChildrenY = bounds.y + geo.y + bounds.height;
+
+			if (layoutBounds == null)
+			{
+				layoutBounds = new mxRectangle(0, childrenY, 0, maxChildrenY - childrenY);
+			}
+			else
+			{
+				layoutBounds.y = Math.min(layoutBounds.y, childrenY);
+				var maxY = Math.max(layoutBounds.y + layoutBounds.height, maxChildrenY);
+				layoutBounds.height = maxY - layoutBounds.y;
+			}
+		}
+	}
+
+	
+	for (var i = 0; i < this.swimlanes.length; i++)
+	{
+		var lane = this.swimlanes[i];
+		var geo = this.graph.getCellGeometry(lane);
+		
+		if (geo != null)
+		{
+			var children = this.graph.getChildCells(lane);
+			
+			var size = (this.graph.isSwimlane(lane)) ?
+					this.graph.getStartSize(lane) : new mxRectangle();
+
+			var newGeo = geo.clone();
+			
+			var leftGroupBorder = (i == 0) ? this.parentBorder : this.interRankCellSpacing/2;
+			newGeo.x += childBounds[i].x - size.width - leftGroupBorder;
+			newGeo.y = newGeo.y + layoutBounds.y - geo.y - this.parentBorder;
+			
+			newGeo.width = childBounds[i].width + size.width + this.interRankCellSpacing/2 + leftGroupBorder;
+			newGeo.height = layoutBounds.height + size.height + 2 * this.parentBorder;
+			
+			this.graph.model.setGeometry(lane, newGeo);
+			this.graph.moveCells(children, -childBounds[i].x + size.width + leftGroupBorder, 
+					geo.y - layoutBounds.y + this.parentBorder);
+		}
+	}
+};
+
+/**
+ * Function: findRoots
+ * 
+ * Returns all visible children in the given parent which do not have
+ * incoming edges. If the result is empty then the children with the
+ * maximum difference between incoming and outgoing edges are returned.
+ * This takes into account edges that are being promoted to the given
+ * root due to invisible children or collapsed cells.
+ * 
+ * Parameters:
+ * 
+ * parent - <mxCell> whose children should be checked.
+ * vertices - array of vertices to limit search to
+ */
+mxSwimlaneLayout.prototype.findRoots = function(parent, vertices)
+{
+	var roots = [];
+	
+	if (parent != null && vertices != null)
+	{
+		var model = this.graph.model;
+		var best = null;
+		var maxDiff = -100000;
+		
+		for (var i in vertices)
+		{
+			var cell = vertices[i];
+
+			if (cell != null && model.isVertex(cell) && this.graph.isCellVisible(cell) && model.isAncestor(parent, cell))
+			{
+				var conns = this.getEdges(cell);
+				var fanOut = 0;
+				var fanIn = 0;
+
+				for (var k = 0; k < conns.length; k++)
+				{
+					var src = this.getVisibleTerminal(conns[k], true);
+
+					if (src == cell)
+					{
+						// Only count connection within this swimlane
+						var other = this.getVisibleTerminal(conns[k], false);
+						
+						if (model.isAncestor(parent, other))
+						{
+							fanOut++;
+						}
+					}
+					else if (model.isAncestor(parent, src))
+					{
+						fanIn++;
+					}
+				}
+
+				if (fanIn == 0 && fanOut > 0)
+				{
+					roots.push(cell);
+				}
+
+				var diff = fanOut - fanIn;
+
+				if (diff > maxDiff)
+				{
+					maxDiff = diff;
+					best = cell;
+				}
+			}
+		}
+		
+		if (roots.length == 0 && best != null)
+		{
+			roots.push(best);
+		}
+	}
+	
+	return roots;
+};
+
+/**
+ * Function: getEdges
+ * 
+ * Returns the connected edges for the given cell.
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> whose edges should be returned.
+ */
+mxSwimlaneLayout.prototype.getEdges = function(cell)
+{
+	var cachedEdges = this.edgesCache.get(cell);
+	
+	if (cachedEdges != null)
+	{
+		return cachedEdges;
+	}
+
+	var model = this.graph.model;
+	var edges = [];
+	var isCollapsed = this.graph.isCellCollapsed(cell);
+	var childCount = model.getChildCount(cell);
+
+	for (var i = 0; i < childCount; i++)
+	{
+		var child = model.getChildAt(cell, i);
+
+		if (this.isPort(child))
+		{
+			edges = edges.concat(model.getEdges(child, true, true));
+		}
+		else if (isCollapsed || !this.graph.isCellVisible(child))
+		{
+			edges = edges.concat(model.getEdges(child, true, true));
+		}
+	}
+
+	edges = edges.concat(model.getEdges(cell, true, true));
+	var result = [];
+	
+	for (var i = 0; i < edges.length; i++)
+	{
+		var source = this.getVisibleTerminal(edges[i], true);
+		var target = this.getVisibleTerminal(edges[i], false);
+		
+		if ((source == target) || ((source != target) && ((target == cell && (this.parent == null || this.graph.isValidAncestor(source, this.parent, this.traverseAncestors))) ||
+			(source == cell && (this.parent == null ||
+					this.graph.isValidAncestor(target, this.parent, this.traverseAncestors))))))
+		{
+			result.push(edges[i]);
+		}
+	}
+
+	this.edgesCache.put(cell, result);
+
+	return result;
+};
+
+/**
+ * Function: getVisibleTerminal
+ * 
+ * Helper function to return visible terminal for edge allowing for ports
+ * 
+ * Parameters:
+ * 
+ * edge - <mxCell> whose edges should be returned.
+ * source - Boolean that specifies whether the source or target terminal is to be returned
+ */
+mxSwimlaneLayout.prototype.getVisibleTerminal = function(edge, source)
+{
+	var terminalCache = this.edgesTargetTermCache;
+	
+	if (source)
+	{
+		terminalCache = this.edgeSourceTermCache;
+	}
+
+	var term = terminalCache.get(edge);
+
+	if (term != null)
+	{
+		return term;
+	}
+
+	var state = this.graph.view.getState(edge);
+	
+	var terminal = (state != null) ? state.getVisibleTerminal(source) : this.graph.view.getVisibleTerminal(edge, source);
+	
+	if (terminal == null)
+	{
+		terminal = (state != null) ? state.getVisibleTerminal(source) : this.graph.view.getVisibleTerminal(edge, source);
+	}
+
+	if (terminal != null)
+	{
+		if (this.isPort(terminal))
+		{
+			terminal = this.graph.model.getParent(terminal);
+		}
+		
+		terminalCache.put(edge, terminal);
+	}
+
+	return terminal;
+};
+
+/**
+ * Function: run
+ * 
+ * The API method used to exercise the layout upon the graph description
+ * and produce a separate description of the vertex position and edge
+ * routing changes made. It runs each stage of the layout that has been
+ * created.
+ */
+mxSwimlaneLayout.prototype.run = function(parent)
+{
+	// Separate out unconnected hierarchies
+	var hierarchyVertices = [];
+	var allVertexSet = [];
+
+	if (this.swimlanes != null && this.swimlanes.length > 0 && parent != null)
+	{
+		var filledVertexSet = Object();
+		
+		for (var i = 0; i < this.swimlanes.length; i++)
+		{
+			this.filterDescendants(this.swimlanes[i], filledVertexSet);
+		}
+
+		this.roots = [];
+		var filledVertexSetEmpty = true;
+
+		// Poor man's isSetEmpty
+		for (var key in filledVertexSet)
+		{
+			if (filledVertexSet[key] != null)
+			{
+				filledVertexSetEmpty = false;
+				break;
+			}
+		}
+
+		// Only test for candidates in each swimlane in order
+		var laneCounter = 0;
+
+		while (!filledVertexSetEmpty && laneCounter < this.swimlanes.length)
+		{
+			var candidateRoots = this.findRoots(this.swimlanes[laneCounter], filledVertexSet);
+			
+			if (candidateRoots.length == 0)
+			{
+				laneCounter++;
+				continue;
+			}
+			
+			// If the candidate root is an unconnected group cell, remove it from
+			// the layout. We may need a custom set that holds such groups and forces
+			// them to be processed for resizing and/or moving.
+			for (var i = 0; i < candidateRoots.length; i++)
+			{
+				var vertexSet = Object();
+				hierarchyVertices.push(vertexSet);
+
+				this.traverse(candidateRoots[i], true, null, allVertexSet, vertexSet,
+						hierarchyVertices, filledVertexSet, laneCounter);
+			}
+
+			for (var i = 0; i < candidateRoots.length; i++)
+			{
+				this.roots.push(candidateRoots[i]);
+			}
+			
+			filledVertexSetEmpty = true;
+			
+			// Poor man's isSetEmpty
+			for (var key in filledVertexSet)
+			{
+				if (filledVertexSet[key] != null)
+				{
+					filledVertexSetEmpty = false;
+					break;
+				}
+			}
+		}
+	}
+	else
+	{
+		// Find vertex set as directed traversal from roots
+
+		for (var i = 0; i < this.roots.length; i++)
+		{
+			var vertexSet = Object();
+			hierarchyVertices.push(vertexSet);
+
+			this.traverse(this.roots[i], true, null, allVertexSet, vertexSet,
+					hierarchyVertices, null);
+		}
+	}
+
+	var tmp = [];
+	
+	for (var key in allVertexSet)
+	{
+		tmp.push(allVertexSet[key]);
+	}
+	
+	this.model = new mxSwimlaneModel(this, tmp, this.roots,
+		parent, this.tightenToSource);
+
+	this.cycleStage(parent);
+	this.layeringStage();
+	
+	this.crossingStage(parent);
+	initialX = this.placementStage(0, parent);
+};
+
+/**
+ * Function: filterDescendants
+ * 
+ * Creates an array of descendant cells
+ */
+mxSwimlaneLayout.prototype.filterDescendants = function(cell, result)
+{
+	var model = this.graph.model;
+
+	if (model.isVertex(cell) && cell != this.parent && model.getParent(cell) != this.parent && this.graph.isCellVisible(cell))
+	{
+		result[mxObjectIdentity.get(cell)] = cell;
+	}
+
+	if (this.traverseAncestors || cell == this.parent
+			&& this.graph.isCellVisible(cell))
+	{
+		var childCount = model.getChildCount(cell);
+
+		for (var i = 0; i < childCount; i++)
+		{
+			var child = model.getChildAt(cell, i);
+			
+			// Ignore ports in the layout vertex list, they are dealt with
+			// in the traversal mechanisms
+			if (!this.isPort(child))
+			{
+				this.filterDescendants(child, result);
+			}
+		}
+	}
+};
+
+/**
+ * Function: isPort
+ * 
+ * Returns true if the given cell is a "port", that is, when connecting to
+ * it, its parent is the connecting vertex in terms of graph traversal
+ * 
+ * Parameters:
+ * 
+ * cell - <mxCell> that represents the port.
+ */
+mxSwimlaneLayout.prototype.isPort = function(cell)
+{
+	if (cell.geometry.relative)
+	{
+		return true;
+	}
+	
+	return false;
+};
+
+/**
+ * Function: getEdgesBetween
+ * 
+ * Returns the edges between the given source and target. This takes into
+ * account collapsed and invisible cells and ports.
+ * 
+ * Parameters:
+ * 
+ * source -
+ * target -
+ * directed -
+ */
+mxSwimlaneLayout.prototype.getEdgesBetween = function(source, target, directed)
+{
+	directed = (directed != null) ? directed : false;
+	var edges = this.getEdges(source);
+	var result = [];
+
+	// Checks if the edge is connected to the correct
+	// cell and returns the first match
+	for (var i = 0; i < edges.length; i++)
+	{
+		var src = this.getVisibleTerminal(edges[i], true);
+		var trg = this.getVisibleTerminal(edges[i], false);
+
+		if ((src == source && trg == target) || (!directed && src == target && trg == source))
+		{
+			result.push(edges[i]);
+		}
+	}
+
+	return result;
+};
+
+/**
+ * Traverses the (directed) graph invoking the given function for each
+ * visited vertex and edge. The function is invoked with the current vertex
+ * and the incoming edge as a parameter. This implementation makes sure
+ * each vertex is only visited once. The function may return false if the
+ * traversal should stop at the given vertex.
+ * 
+ * Parameters:
+ * 
+ * vertex - <mxCell> that represents the vertex where the traversal starts.
+ * directed - boolean indicating if edges should only be traversed
+ * from source to target. Default is true.
+ * edge - Optional <mxCell> that represents the incoming edge. This is
+ * null for the first step of the traversal.
+ * allVertices - Array of cell paths for the visited cells.
+ * swimlaneIndex - the laid out order index of the swimlane vertex is contained in
+ */
+mxSwimlaneLayout.prototype.traverse = function(vertex, directed, edge, allVertices, currentComp,
+											hierarchyVertices, filledVertexSet, swimlaneIndex)
+{
+	if (vertex != null && allVertices != null)
+	{
+		// Has this vertex been seen before in any traversal
+		// And if the filled vertex set is populated, only 
+		// process vertices in that it contains
+		var vertexID = mxObjectIdentity.get(vertex);
+		
+		if ((allVertices[vertexID] == null)
+				&& (filledVertexSet == null ? true : filledVertexSet[vertexID] != null))
+		{
+			if (currentComp[vertexID] == null)
+			{
+				currentComp[vertexID] = vertex;
+			}
+			if (allVertices[vertexID] == null)
+			{
+				allVertices[vertexID] = vertex;
+			}
+
+			if (filledVertexSet !== null)
+			{
+				delete filledVertexSet[vertexID];
+			}
+
+			var edges = this.getEdges(vertex);
+			var model = this.graph.model;
+
+			for (var i = 0; i < edges.length; i++)
+			{
+				var otherVertex = this.getVisibleTerminal(edges[i], true);
+				var isSource = otherVertex == vertex;
+				
+				if (isSource)
+				{
+					otherVertex = this.getVisibleTerminal(edges[i], false);
+				}
+
+				var otherIndex = 0;
+				// Get the swimlane index of the other terminal
+				for (otherIndex = 0; otherIndex < this.swimlanes.length; otherIndex++)
+				{
+					if (model.isAncestor(this.swimlanes[otherIndex], otherVertex))
+					{
+						break;
+					}
+				}
+				
+				if (otherIndex >= this.swimlanes.length)
+				{
+					continue;
+				}
+
+				// Traverse if the other vertex is within the same swimlane as
+				// as the current vertex, or if the swimlane index of the other
+				// vertex is greater than that of this vertex
+				if ((otherIndex > swimlaneIndex) ||
+						((!directed || isSource) && otherIndex == swimlaneIndex))
+				{
+					currentComp = this.traverse(otherVertex, directed, edges[i], allVertices,
+							currentComp, hierarchyVertices,
+							filledVertexSet, otherIndex);
+				}
+			}
+		}
+		else
+		{
+			if (currentComp[vertexID] == null)
+			{
+				// We've seen this vertex before, but not in the current component
+				// This component and the one it's in need to be merged
+				for (var i = 0; i < hierarchyVertices.length; i++)
+				{
+					var comp = hierarchyVertices[i];
+
+					if (comp[vertexID] != null)
+					{
+						for (var key in comp)
+						{
+							currentComp[key] = comp[key];
+						}
+						
+						// Remove the current component from the hierarchy set
+						hierarchyVertices.splice(i, 1);
+						return currentComp;
+					}
+				}
+			}
+		}
+	}
+	
+	return currentComp;
+};
+
+/**
+ * Function: cycleStage
+ * 
+ * Executes the cycle stage using mxMinimumCycleRemover.
+ */
+mxSwimlaneLayout.prototype.cycleStage = function(parent)
+{
+	var cycleStage = new mxSwimlaneOrdering(this);
+	cycleStage.execute(parent);
+};
+
+/**
+ * Function: layeringStage
+ * 
+ * Implements first stage of a Sugiyama layout.
+ */
+mxSwimlaneLayout.prototype.layeringStage = function()
+{
+	this.model.initialRank();
+	this.model.fixRanks();
+};
+
+/**
+ * Function: crossingStage
+ * 
+ * Executes the crossing stage using mxMedianHybridCrossingReduction.
+ */
+mxSwimlaneLayout.prototype.crossingStage = function(parent)
+{
+	var crossingStage = new mxMedianHybridCrossingReduction(this);
+	crossingStage.execute(parent);
+};
+
+/**
+ * Function: placementStage
+ * 
+ * Executes the placement stage using mxCoordinateAssignment.
+ */
+mxSwimlaneLayout.prototype.placementStage = function(initialX, parent)
+{
+	var placementStage = new mxCoordinateAssignment(this, this.intraCellSpacing,
+			this.interRankCellSpacing, this.orientation, initialX,
+			this.parallelEdgeSpacing);
+	placementStage.fineTuning = this.fineTuning;
+	placementStage.execute(parent);
+	
+	return placementStage.limitX + this.interHierarchySpacing;
+};
diff --git a/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/stage/mxCoordinateAssignment.js b/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/stage/mxCoordinateAssignment.js
new file mode 100644
index 0000000..70c9bbd
--- /dev/null
+++ b/airavata-kubernetes/web-console/src/assets/js/layout/hierarchical/stage/mxCoordinateAssignment.js
@@ -0,0 +1,1830 @@
+/**
+ * Copyright (c) 2006-2015, JGraph Ltd
+ * Copyright (c) 2006-2015, Gaudenz Alder
+ */
+/**
+ * Class: mxCoordinateAssignment
+ * 
+ * Sets the horizontal locations of node and edge dummy nodes on each layer.
+ * Uses median down and up weighings as well as heuristics to straighten edges as
+ * far as possible.
+ * 
+ * Constructor: mxCoordinateAssignment
+ *
+ * Creates a coordinate assignment.
+ * 
+ * Arguments:
+ * 
+ * intraCellSpacing - the minimum buffer between cells on the same rank
+ * interRankCellSpacing - the minimum distance between cells on adjacent ranks
+ * orientation - the position of the root node(s) relative to the graph
+ * initialX - the leftmost coordinate node placement starts at
+ */
+function mxCoordinateAssignment(layout, intraCellSpacing, interRankCellSpacing,
+	orientation, initialX, parallelEdgeSpacing)
+{
+	this.layout = layout;
+	this.intraCellSpacing = intraCellSpacing;
+	this.interRankCellSpacing = interRankCellSpacing;
+	this.orientation = orientation;
+	this.initialX = initialX;
+	this.parallelEdgeSpacing = parallelEdgeSpacing;
+};
+
+/**
+ * Extends mxHierarchicalLayoutStage.
+ */
+mxCoordinateAssignment.prototype = new mxHierarchicalLayoutStage();
+mxCoordinateAssignment.prototype.constructor = mxCoordinateAssignment;
+
+/**
+ * Variable: layout
+ * 
+ * Reference to the enclosing <mxHierarchicalLayout>.
+ */
+mxCoordinateAssignment.prototype.layout = null;
+
+/**
+ * Variable: intraCellSpacing
+ * 
+ * The minimum buffer between cells on the same rank. Default is 30.
+ */
+mxCoordinateAssignment.prototype.intraCellSpacing = 30;
+
+/**
+ * Variable: interRankCellSpacing
+ * 
+ * The minimum distance between cells on adjacent ranks. Default is 10.
+ */
+mxCoordinateAssignment.prototype.interRankCellSpacing = 100;
+
+/**
+ * Variable: parallelEdgeSpacing
+ * 
+ * The distance between each parallel edge on each ranks for long edges.
+ * Default is 10.
+ */
+mxCoordinateAssignment.prototype.parallelEdgeSpacing = 10;
+
+/**
+ * Variable: maxIterations
+ * 
+ * The number of heuristic iterations to run. Default is 8.
+ */
+mxCoordinateAssignment.prototype.maxIterations = 8;
+
+/**
+ * Variable: prefHozEdgeSep
+ * 
+ * The preferred horizontal distance between edges exiting a vertex
+ */
+mxCoordinateAssignment.prototype.prefHozEdgeSep = 5;
+
+/**
+ * Variable: prefVertEdgeOff
+ * 
+ * The preferred vertical offset between edges exiting a vertex
+ */
+mxCoordinateAssignment.prototype.prefVertEdgeOff = 2;
+
+/**
+ * Variable: minEdgeJetty
+ * 
+ * The minimum distance for an edge jetty from a vertex
+ */
+mxCoordinateAssignment.prototype.minEdgeJetty = 12;
+
+/**
+ * Variable: channelBuffer
+ * 
+ * The size of the vertical buffer in the center of inter-rank channels
+ * where edge control points should not be placed
+ */
+mxCoordinateAssignment.prototype.channelBuffer = 4;
+
+/**
+ * Variable: jettyPositions
+ * 
+ * Map of internal edges and (x,y) pair of positions of the start and end jetty
+ * for that edge where it connects to the source and target vertices.
+ * Note this should technically be a WeakHashMap, but since JS does not
+ * have an equivalent, housekeeping must be performed before using.
+ * i.e. check all edges are still in the model and clear the values.
+ * Note that the y co-ord is the offset of the jetty, not the
+ * absolute point
+ */
+mxCoordinateAssignment.prototype.jettyPositions = null;
+
+/**
+ * Variable: orientation
+ * 
+ * The position of the root ( start ) node(s) relative to the rest of the
+ * laid out graph. Default is <mxConstants.DIRECTION_NORTH>.
+ */
+mxCoordinateAssignment.prototype.orientation = mxConstants.DIRECTION_NORTH;
+
+/**
+ * Variable: initialX
+ * 
+ * The minimum x position node placement starts at
+ */
+mxCoordinateAssignment.prototype.initialX = null;
+
+/**
+ * Variable: limitX
+ * 
+ * The maximum x value this positioning lays up to
+ */
+mxCoordinateAssignment.prototype.limitX = null;
+
+/**
+ * Variable: currentXDelta
+ * 
+ * The sum of x-displacements for the current iteration
+ */
+mxCoordinateAssignment.prototype.currentXDelta = null;
+
+/**
+ * Variable: widestRank
+ * 
+ * The rank that has the widest x position
+ */
+mxCoordinateAssignment.prototype.widestRank = null;
+
+/**
+ * Variable: rankTopY
+ * 
+ * Internal cache of top-most values of Y for each rank
+ */
+mxCoordinateAssignment.prototype.rankTopY = null;
+
+/**
+ * Variable: rankBottomY
+ * 
+ * Internal cache of bottom-most value of Y for each rank
+ */
+mxCoordinateAssignment.prototype.rankBottomY = null;
+
+/**
+ * Variable: widestRankValue
+ * 
+ * The X-coordinate of the edge of the widest rank
+ */
+mxCoordinateAssignment.prototype.widestRankValue = null;
+
+/**
+ * Variable: rankWidths
+ * 
+ * The width of all the ranks
+ */
+mxCoordinateAssignment.prototype.rankWidths = null;
+
+/**
+ * Variable: rankY
+ * 
+ * The Y-coordinate of all the ranks
+ */
+mxCoordinateAssignment.prototype.rankY = null;
+
+/**
+ * Variable: fineTuning
+ * 
+ * Whether or not to perform local optimisations and iterate multiple times
+ * through the algorithm. Default is true.
+ */
+mxCoordinateAssignment.prototype.fineTuning = true;
+
+/**
+ * Variable: nextLayerConnectedCache
+ * 
+ * A store of connections to the layer above for speed
+ */
+mxCoordinateAssignment.prototype.nextLayerConnectedCache = null;
+
+/**
+ * Variable: previousLayerConnectedCache
+ * 
+ * A store of connections to the layer below for speed
+ */
+mxCoordinateAssignment.prototype.previousLayerConnectedCache = null;
+
+/**
+ * Variable: groupPadding
+ * 
+ * Padding added to resized parents
+ */
+mxCoordinateAssignment.prototype.groupPadding = 10;
+
+/**
+ * Utility method to display current positions
+ */
+mxCoordinateAssignment.prototype.printStatus = function()
+{
+	var model = this.layout.getModel();
+	mxLog.show();
+
+	mxLog.writeln('======Coord assignment debug=======');
+
+	for (var j = 0; j < model.ranks.length; j++)
+	{
+		mxLog.write('Rank ', j, ' : ' );
+		var rank = model.ranks[j];
+		
+		for (var k = 0; k < rank.length; k++)
+		{
+			var cell = rank[k];
+			
+			mxLog.write(cell.getGeneralPurposeVariable(j), '  ');
+		}
+		mxLog.writeln();
+	}
+	
+	mxLog.writeln('====================================');
+};
+
+/**
+ * Function: execute
+ * 
+ * A basic horizontal coordinate assignment algorithm
+ */
+mxCoordinateAssignment.prototype.execute = function(parent)
+{
+	this.jettyPositions = Object();
+	var model = this.layout.getModel();
+	this.currentXDelta = 0.0;
+
+	this.initialCoords(this.layout.getGraph(), model);
+	
+//	this.printStatus();
+	
+	if (this.fineTuning)
+	{
+		this.minNode(model);
+	}
+	
+	var bestXDelta = 100000000.0;
+	
+	if (this.fineTuning)
+	{
+		for (var i = 0; i < this.maxIterations; i++)
+		{
+//			this.printStatus();
+		
+			// Median Heuristic
+			if (i != 0)
+			{
+				this.medianPos(i, model);
+				this.minNode(model);
+			}
+			
+			// if the total offset is less for the current positioning,
+			// there are less heavily angled edges and so the current
+			// positioning is used
+			if (this.currentXDelta < bestXDelta)
+			{
+				for (var j = 0; j < model.ranks.length; j++)
+				{
+					var rank = model.ranks[j];
+					
+					for (var k = 0; k < rank.length; k++)
+					{
+						var cell = rank[k];
+						cell.setX(j, cell.getGeneralPurposeVariable(j));
+					}
+				}
+				
+				bestXDelta = this.currentXDelta;
+			}
+			else
+			{
+				// Restore the best positions
+				for (var j = 0; j < model.ranks.length; j++)
+				{
+					var rank = model.ranks[j];
+					
+					for (var k = 0; k < rank.length; k++)
+					{
+						var cell = rank[k];
+						cell.setGeneralPurposeVariable(j, cell.getX(j));
+					}
+				}
+			}
+			
+			this.minPath(this.layout.getGraph(), model);
+			
+			this.currentXDelta = 0;
+		}
+	}
+	
+	this.setCellLocations(this.layout.getGraph(), model);
+};
+
+/**
+ * Function: minNode
+ * 
+ * Performs one median positioning sweep in both directions
+ */
+mxCoordinateAssignment.prototype.minNode = function(model)
+{
+	// Queue all nodes
+	var nodeList = [];
+	
+	// Need to be able to map from cell to cellWrapper
+	var map = new mxDictionary();
+	var rank = [];
+	
+	for (var i = 0; i <= model.maxRank; i++)
+	{
+		rank[i] = model.ranks[i];
+		
+		for (var j = 0; j < rank[i].length; j++)
+		{
+			// Use the weight to store the rank and visited to store whether
+			// or not the cell is in the list
+			var node = rank[i][j];
+			var nodeWrapper = new WeightedCellSorter(node, i);
+			nodeWrapper.rankIndex = j;
+			nodeWrapper.visited = true;
+			nodeList.push(nodeWrapper);
+			
+			map.put(node, nodeWrapper);
+		}
+	}
+	
+	// Set a limit of the maximum number of times we will access the queue
+	// in case a loop appears
+	var maxTries = nodeList.length * 10;
+	var count = 0;
+	
+	// Don't move cell within this value of their median
+	var tolerance = 1;
+	
+	while (nodeList.length > 0 && count <= maxTries)
+	{
+		var cellWrapper = nodeList.shift();
+		var cell = cellWrapper.cell;
+		
+		var rankValue = cellWrapper.weightedValue;
+		var rankIndex = parseInt(cellWrapper.rankIndex);
+		
+		var nextLayerConnectedCells = cell.getNextLayerConnectedCells(rankValue);
+		var previousLayerConnectedCells = cell.getPreviousLayerConnectedCells(rankValue);
+		
+		var numNextLayerConnected = nextLayerConnectedCells.length;
+		var numPreviousLayerConnected = previousLayerConnectedCells.length;
+
+		var medianNextLevel = this.medianXValue(nextLayerConnectedCells,
+				rankValue + 1);
+		var medianPreviousLevel = this.medianXValue(previousLayerConnectedCells,
+				rankValue - 1);
+
+		var numConnectedNeighbours = numNextLayerConnected
+				+ numPreviousLayerConnected;
+		var currentPosition = cell.getGeneralPurposeVariable(rankValue);
+		var cellMedian = currentPosition;
+		
+		if (numConnectedNeighbours > 0)
+		{
+			cellMedian = (medianNextLevel * numNextLayerConnected + medianPreviousLevel
+					* numPreviousLayerConnected)
+					/ numConnectedNeighbours;
+		}
+
+		// Flag storing whether or not position has changed
+		var positionChanged = false;
+		
+		if (cellMedian < currentPosition - tolerance)
+		{
+			if (rankIndex == 0)
+			{
+				cell.setGeneralPurposeVariable(rankValue, cellMedian);
+				positionChanged = true;
+			}
+			else
+			{
+				var leftCell = rank[rankValue][rankIndex - 1];
+				var leftLimit = leftCell
+						.getGeneralPurposeVariable(rankValue);
+				leftLimit = leftLimit + leftCell.width / 2
+						+ this.intraCellSpacing + cell.width / 2;
+
+				if (leftLimit < cellMedian)
+				{
+					cell.setGeneralPurposeVariable(rankValue, cellMedian);
+					positionChanged = true;
+				}
+				else if (leftLimit < cell
+						.getGeneralPurposeVariable(rankValue)
+						- tolerance)
+				{
+					cell.setGeneralPurposeVariable(rankValue, leftLimit);
+					positionChanged = true;
+				}
+			}
+		}
+		else if (cellMedian > currentPosition + tolerance)
+		{
+			var rankSize = rank[rankValue].length;
+			
+			if (rankIndex == rankSize - 1)
+			{
+				cell.setGeneralPurposeVariable(rankValue, cellMedian);
+				positionChanged = true;
+			}
+			else
+			{
+				var rightCell = rank[rankValue][rankIndex + 1];
+				var rightLimit = rightCell
+						.getGeneralPurposeVariable(rankValue);
+				rightLimit = rightLimit - rightCell.width / 2
+						- this.intraCellSpacing - cell.width / 2;
+				
+				if (rightLimit > cellMedian)
+				{
+					cell.setGeneralPurposeVariable(rankValue, cellMedian);
+					positionChanged = true;
+				}
+				else if (rightLimit > cell
+						.getGeneralPurposeVariable(rankValue)
+						+ tolerance)
+				{
+					cell.setGeneralPurposeVariable(rankValue, rightLimit);
+					positionChanged = true;
+				}
+			}
+		}
+		
+		if (positionChanged)
+		{
+			// Add connected nodes to map and list
+			for (var i = 0; i < nextLayerConnectedCells.length; i++)
+			{
+				var connectedCell = nextLayerConnectedCells[i];
+				var connectedCellWrapper = map.get(connectedCell);
+				
+				if (connectedCellWrapper != null)
+				{
+					if (connectedCellWrapper.visited == false)
+					{
+						connectedCellWrapper.visited = true;
+						nodeList.push(connectedCellWrapper);
+					}
+				}
+			}
+
+			// Add connected nodes to map and list
+			for (var i = 0; i < previousLayerConnectedCells.length; i++)
+			{
+				var connectedCell = previousLayerConnectedCells[i];
+				var connectedCellWrapper = map.get(connectedCell);
+
+				if (connectedCellWrapper != null)
+				{
+					if (connectedCellWrapper.visited == false)
+					{
+						connectedCellWrapper.visited = true;
+						nodeList.push(connectedCellWrapper);
+					}
+				}
+			}
+		}
+		
+		cellWrapper.visited = false;
+		count++;
+	}
+};
+
+/**
+ * Function: medianPos
+ * 
+ * Performs one median positioning sweep in one direction
+ * 
+ * Parameters:
+ * 
+ * i - the iteration of the whole process
+ * model - an internal model of the hierarchical layout
+ */
+mxCoordinateAssignment.prototype.medianPos = function(i, model)
+{
+	// Reverse sweep direction each time through this method
+	var downwardSweep = (i % 2 == 0);
+	
+	if (downwardSweep)
+	{
+		for (var j = model.maxRank; j > 0; j--)
+		{
+			this.rankMedianPosition(j - 1, model, j);
+		}
+	}
+	else
+	{
+		for (var j = 0; j < model.maxRank - 1; j++)
+		{
+			this.rankMedianPosition(j + 1, model, j);
+		}
+	}
+};
+
+/**
+ * Function: rankMedianPosition
+ * 
+ * Performs median minimisation over one rank.
+ * 
+ * Parameters:
+ * 
+ * rankValue - the layer number of this rank
+ * model - an internal model of the hierarchical layout
+ * nextRankValue - the layer number whose connected cels are to be laid out
+ * relative to
+ */
+mxCoordinateAssignment.prototype.rankMedianPosition = function(rankValue, model, nextRankValue)
+{
+	var rank = model.ranks[rankValue];
+
+	// Form an array of the order in which the cell are to be processed
+	// , the order is given by the weighted sum of the in or out edges,
+	// depending on whether we're traveling up or down the hierarchy.
+	var weightedValues = [];
+	var cellMap = new Object();
+
+	for (var i = 0; i < rank.length; i++)
+	{
+		var currentCell = rank[i];
+		weightedValues[i] = new WeightedCellSorter();
+		weightedValues[i].cell = currentCell;
+		weightedValues[i].rankIndex = i;
+		cellMap[currentCell.id] = weightedValues[i];
+		var nextLayerConnectedCells = null;
+		
+		if (nextRankValue < rankValue)
+		{
+			nextLayerConnectedCells = currentCell
+					.getPreviousLayerConnectedCells(rankValue);
+		}
+		else
+		{
+			nextLayerConnectedCells = currentCell
+					.getNextLayerConnectedCells(rankValue);
+		}
+
+		// Calculate the weighing based on this node type and those this
+		// node is connected to on the next layer
+		weightedValues[i].weightedValue = this.calculatedWeightedValue(
+				currentCell, nextLayerConnectedCells);
+	}
+
+	weightedValues.sort(WeightedCellSorter.prototype.compare);
+
+	// Set the new position of each node within the rank using
+	// its temp variable
+	
+	for (var i = 0; i < weightedValues.length; i++)
+	{
+		var numConnectionsNextLevel = 0;
+		var cell = weightedValues[i].cell;
+		var nextLayerConnectedCells = null;
+		var medianNextLevel = 0;
+
+		if (nextRankValue < rankValue)
+		{
+			nextLayerConnectedCells = cell.getPreviousLayerConnectedCells(
+					rankValue).slice();
+		}
+		else
+		{
+			nextLayerConnectedCells = cell.getNextLayerConnectedCells(
+					rankValue).slice();
+		}
+
+		if (nextLayerConnectedCells != null)
+		{
+			numConnectionsNextLevel = nextLayerConnectedCells.length;
+			
+			if (numConnectionsNextLevel > 0)
+			{
+				medianNextLevel = this.medianXValue(nextLayerConnectedCells,
+						nextRankValue);
+			}
+			else
+			{
+				// For case of no connections on the next level set the
+				// median to be the current position and try to be
+				// positioned there
+				medianNextLevel = cell.getGeneralPurposeVariable(rankValue);
+			}
+		}
+
+		var leftBuffer = 0.0;
+		var leftLimit = -100000000.0;
+		
+		for (var j = weightedValues[i].rankIndex - 1; j >= 0;)
+		{
+			var weightedValue = cellMap[rank[j].id];
+			
+			if (weightedValue != null)
+			{
+				var leftCell = weightedValue.cell;
+				
+				if (weightedValue.visited)
+				{
+					// The left limit is the right hand limit of that
+					// cell plus any allowance for unallocated cells
+					// in-between
+					leftLimit = leftCell
+							.getGeneralPurposeVariable(rankValue)
+							+ leftCell.width
+							/ 2.0
+							+ this.intraCellSpacing
+							+ leftBuffer + cell.width / 2.0;
+					j = -1;
+				}
+				else
+				{
+					leftBuffer += leftCell.width + this.intraCellSpacing;
+					j--;
+				}
+			}
+		}
+
+		var rightBuffer = 0.0;
+		var rightLimit = 100000000.0;
+		
+		for (var j = weightedValues[i].rankIndex + 1; j < weightedValues.length;)
+		{
+			var weightedValue = cellMap[rank[j].id];
+			
+			if (weightedValue != null)
+			{
+				var rightCell = weightedValue.cell;
+				
+				if (weightedValue.visited)
+				{
+					// T