소스 검색

HUE-3227 [connector] Java snippet

Romain Rigaux 9 년 전
부모
커밋
b67941a2e4

+ 217 - 252
apps/oozie/src/oozie/models2.py

@@ -2030,6 +2030,112 @@ class HiveDocumentAction(Action):
     return [cls.FIELDS['uuid']]
 
 
+class JavaDocumentAction(Action):
+  TYPE = 'java-document'
+  FIELDS = {
+     'uuid': {
+          'name': 'uuid',
+          'label': _('Java program'),
+          'value': '',
+          'help_text': _('Select a saved Java program you want to schedule.'),
+          'type': 'hive'
+     },
+     'jar_path': {
+          'name': 'jar_path',
+          'label': _('Jar name'),
+          'value': '',
+          'help_text': _('Path to the jar on HDFS.'),
+          'type': ''
+     },
+     'main_class': {
+          'name': 'main_class',
+          'label': _('Main class'),
+          'value': '',
+          'help_text': _('Java class. e.g. org.apache.hadoop.examples.Grep'),
+          'type': 'text'
+     },
+     'arguments': {
+          'name': 'arguments',
+          'label': _('Arguments'),
+          'value': [],
+          'help_text': _('Arguments of the main method. The value of each arg element is considered a single argument '
+                         'and they are passed to the main method in the same order.'),
+          'type': ''
+     },
+     'java_opts': {
+          'name': 'java_opts',
+          'label': _('Java options'),
+          'value': [],
+          'help_text': _('Parameters for the JVM, e.g. -Dprop1=a -Dprop2=b'),
+          'type': ''
+     },
+     'capture_output': {
+          'name': 'capture_output',
+          'label': _('Capture output'),
+          'value': False,
+          'help_text': _('Capture output of the stdout of the %(program)s command execution. The %(program)s '
+                         'command output must be in Java Properties file format and it must not exceed 2KB. '
+                         'From within the workflow definition, the output of an %(program)s action node is accessible '
+                         'via the String action:output(String node, String key) function') % {'program': TYPE.title()},
+          'type': ''
+     },
+     # Common
+     'files': {
+          'name': 'files',
+          'label': _('Files'),
+          'value': [],
+          'help_text': _('Files put in the running directory.'),
+          'type': ''
+     },
+     'archives': {
+          'name': 'archives',
+          'label': _('Archives'),
+          'value': [],
+          'help_text': _('zip, tar and tgz/tar.gz uncompressed into the running directory.'),
+          'type': ''
+     },
+     'job_properties': {
+          'name': 'job_properties',
+          'label': _('Hadoop job properties'),
+          'value': [],
+          'help_text': _('value, e.g. production'),
+          'type': ''
+     },
+     'prepares': {
+          'name': 'prepares',
+          'label': _('Prepares'),
+          'value': [],
+          'help_text': _('Path to manipulate before starting the application.'),
+          'type': ''
+     },
+     'job_xml': {
+          'name': 'job_xml',
+          'label': _('Job XML'),
+          'value': [],
+          'help_text': _('Refer to a Hadoop JobConf job.xml'),
+          'type': ''
+     },
+     'retry_max': {
+          'name': 'retry_max',
+          'label': _('Max retry'),
+          'value': [],
+          'help_text': _('Number of times, default is 3'),
+          'type': ''
+     },
+     'retry_interval': {
+          'name': 'retry_interval',
+          'label': _('Retry interval'),
+          'value': [],
+          'help_text': _('Wait time in minutes, default is 10'),
+          'type': ''
+     }
+  }
+
+  @classmethod
+  def get_mandatory_fields(cls):
+    return [cls.FIELDS['uuid']]
+
+
 class DecisionNode(Action):
   TYPE = 'decision'
   FIELDS = {}
@@ -2061,7 +2167,8 @@ NODES = {
   'decision-widget': DecisionNode,
   'spark-widget': SparkAction,
   'generic-widget': GenericAction,
-  'hive-document-widget': HiveDocumentAction
+  'hive-document-widget': HiveDocumentAction,
+  'java-document-widget': JavaDocumentAction
 }
 
 
@@ -2813,27 +2920,122 @@ class WorkflowBuilder():
   """
 
   def create_workflow(self, document, user, name=None, managed=False):
-    parameters = self.get_document_parameters(document)
 
     if name is None:
-      name = _('Schedule of ') + document.name
+      name = _('Schedule of ') + document.name or document.type
 
-    workflow_doc = self.create_hive_document_workflow(name, document.uuid, parameters, user, managed=managed)
+    if document.type == 'java':
+      node = self.get_java_document_node(document, name, document.uuid)
+    else:
+      node = self.get_hive_document_node(document, name, document.uuid, user)
+
+    workflow_doc = self.get_workflow(node, name, document.uuid, user, managed=managed)
     workflow_doc.dependencies.add(document)
 
     return workflow_doc
 
-  def get_document_parameters(self, document):
+
+  def get_hive_document_node(self, document, name, doc_uuid, parameters, user):
+    api = get_oozie(user)
+
+    credentials = [HiveDocumentAction.DEFAULT_CREDENTIALS] if api.security_enabled else []
+
     notebook = Notebook(document=document)
     parameters = find_dollar_braced_variables(notebook.get_str())
+    parameters = [{u'value': u'%s=${%s}' % (p, p)} for p in parameters]
 
-    return [{u'value': u'%s=${%s}' % (p, p)} for p in parameters]
+    return {
+        u'name': u'doc-1',
+        u'actionParametersUI': [],
+        u'properties': {
+            u'files': [],
+            u'job_xml': u'',
+            u'uuid': doc_uuid,
+            u'parameters': parameters,
+            u'retry_interval': [],
+            u'retry_max': [],
+            u'job_properties': [],
+            u'sla': [
+                {u'key': u'enabled', u'value': False},
+                {u'key': u'nominal-time', u'value': u'${nominal_time}'},
+                {u'key': u'should-start', u'value': u''},
+                {u'key': u'should-end', u'value': u'${30 * MINUTES}'},
+                {u'key': u'max-duration', u'value': u''},
+                {u'key': u'alert-events', u'value': u''},
+                {u'key': u'alert-contact', u'value': u''},
+                {u'key': u'notification-msg', u'value': u''},
+                {u'key': u'upstream-apps', u'value': u''},
+                ],
+            u'archives': [],
+            u'prepares': [],
+            u'credentials': credentials,
+            u'password': u'',
+            u'jdbc_url': u'',
+            },
+        u'actionParametersFetched': False,
+        u'id': u'0aec471d-2b7c-d93d-b22c-2110fd17ea2c',
+        u'type': u'hive-document-widget',
+        u'children': [{u'to': u'33430f0f-ebfa-c3ec-f237-3e77efa03d0a'},
+                      {u'error': u'17c9c895-5a16-7443-bb81-f34b30b21548'
+                      }],
+        u'actionParameters': [],
+    }
 
-  def create_hive_document_workflow(self, name, doc_uuid, parameters, user, managed=False):
-    api = get_oozie(user)
+  def get_java_document_node(self, document, node, name, doc_uuid, parameters):
+    credentials = []
+
+    java_class = 'org.apache.solr.hadoop.MapReduceIndexerTool'
+    arguments = [
+        {"value": "--morphline-file"},
+        {"value": "morphline.conf"},
+        {"value": "--output-dir"},
+        {"value": "${nameNode}${outputDir}"},
+        {"value": "--log4j"},
+        {"value": "log4j.properties"},
+        {"value": "--verbose"},
+        {"value": "--go-live"},
+        {"value": "--zk-host"},
+        {"value": "${zkHost}"},
+        {"value": "--collection"},
+        {"value": "${collectionName}"},
+        {"value": "${nameNode}${filePath}"}
+    ]
+    files = [
+        {"value": "morphline.conf#morphline.conf"},
+        {"value": "log4j.properties#log4j.properties"},
+    ]
 
-    credentials = [HiveDocumentAction.DEFAULT_CREDENTIALS] if api.security_enabled else []
+    return {
+         "id":"0aec471d-2b7c-d93d-b22c-2110fd17ea2c",
+         "name":"doc-1",
+         "type":"java-widget",
+         "properties":{
+              "files": files,
+              "job_xml":[],
+              "jar_path":"jar",
+              "java_opts":[],
+              "retry_max":[],
+              "retry_interval":[],
+              "job_properties":[],
+              "capture_output": False,
+              "main_class": java_class,
+              "arguments": arguments,
+              "prepares":[],
+              "credentials": credentials,
+              "sla":[{"value":False, "key":"enabled"}, {"value":"${nominal_time}", "key":"nominal-time"}, {"value":"", "key":"should-start"}, {"value":"${30 * MINUTES}", "key":"should-end"}, {"value":"", "key":"max-duration"}, {"value":"", "key":"alert-events"}, {"value":"", "key":"alert-contact"}, {"value":"", "key":"notification-msg"}, {"value":"", "key":"upstream-apps"}],
+              "archives":[]
+          },
+          "children":[
+              {"to":"33430f0f-ebfa-c3ec-f237-3e77efa03d0a"},
+              {"error":"17c9c895-5a16-7443-bb81-f34b30b21548"}],
+          "actionParameters":[],
+          "actionParametersFetched": False
+    }
+    
+    
 
+  def get_workflow(self, name, doc_uuid, user, managed=False):    
+    
     data = json.dumps({'workflow': {
       u'name': name,
       u'versions': [u'uri:oozie:workflow:0.4', u'uri:oozie:workflow:0.4.5'
@@ -2886,42 +3088,9 @@ class WorkflowBuilder():
           u'type': u'kill-widget',
           u'children': [],
           u'actionParameters': [],
-          }, {
-          u'name': u'hive-0aec',
-          u'actionParametersUI': [],
-          u'properties': {
-              u'files': [],
-              u'job_xml': u'',
-              u'uuid': doc_uuid,
-              u'parameters': parameters,
-              u'retry_interval': [],
-              u'retry_max': [],
-              u'job_properties': [],
-              u'sla': [
-                  {u'key': u'enabled', u'value': False},
-                  {u'key': u'nominal-time', u'value': u'${nominal_time}'},
-                  {u'key': u'should-start', u'value': u''},
-                  {u'key': u'should-end', u'value': u'${30 * MINUTES}'},
-                  {u'key': u'max-duration', u'value': u''},
-                  {u'key': u'alert-events', u'value': u''},
-                  {u'key': u'alert-contact', u'value': u''},
-                  {u'key': u'notification-msg', u'value': u''},
-                  {u'key': u'upstream-apps', u'value': u''},
-                  ],
-              u'archives': [],
-              u'prepares': [],
-              u'credentials': credentials,
-              u'password': u'',
-              u'jdbc_url': u'',
-              },
-          u'actionParametersFetched': False,
-          u'id': u'0aec471d-2b7c-d93d-b22c-2110fd17ea2c',
-          u'type': u'hive-document-widget',
-          u'children': [{u'to': u'33430f0f-ebfa-c3ec-f237-3e77efa03d0a'},
-                        {u'error': u'17c9c895-5a16-7443-bb81-f34b30b21548'
-                        }],
-          u'actionParameters': [],
-          }],
+          },
+            node 
+          ],
       u'properties': {
           u'job_xml': u'',
           u'description': u'',
@@ -2948,216 +3117,12 @@ class WorkflowBuilder():
       u'nodeNamesMapping': {
           u'33430f0f-ebfa-c3ec-f237-3e77efa03d0a': u'End',
           u'3f107997-04cc-8733-60a9-a4bb62cebffc': u'Start',
-          u'0aec471d-2b7c-d93d-b22c-2110fd17ea2c': u'hive-0aec',
+          u'0aec471d-2b7c-d93d-b22c-2110fd17ea2c': u'doc-1',
           u'17c9c895-5a16-7443-bb81-f34b30b21548': u'Kill',
           },
       u'uuid': u'433922e5-e616-dfe0-1cba-7fe744c9305c',
-      }, 'layout': [{
-      u'oozieRows': [{
-          u'enableOozieDropOnBefore': True,
-          u'enableOozieDropOnSide': True,
-          u'enableOozieDrop': False,
-          u'widgets': [{
-              u'status': u'',
-              u'logsURL': u'',
-              u'name': u'Hive',
-              u'widgetType': u'hive-document-widget',
-              u'oozieMovable': True,
-              u'ooziePropertiesExpanded': False,
-              u'externalIdUrl': u'',
-              u'properties': {},
-              u'isLoading': True,
-              u'offset': 0,
-              u'actionURL': u'',
-              u'progress': 0,
-              u'klass': u'card card-widget span12',
-              u'oozieExpanded': False,
-              u'id': u'0aec471d-2b7c-d93d-b22c-2110fd17ea2c',
-              u'size': 12,
-              }],
-          u'id': u'32e1ea1a-812b-6878-9719-ff7b8407bf46',
-          u'columns': [],
-          }],
-      u'rows': [{
-          u'enableOozieDropOnBefore': True,
-          u'enableOozieDropOnSide': True,
-          u'enableOozieDrop': False,
-          u'widgets': [{
-              u'status': u'',
-              u'logsURL': u'',
-              u'name': u'Start',
-              u'widgetType': u'start-widget',
-              u'oozieMovable': False,
-              u'ooziePropertiesExpanded': False,
-              u'externalIdUrl': u'',
-              u'properties': {},
-              u'isLoading': True,
-              u'offset': 0,
-              u'actionURL': u'',
-              u'progress': 0,
-              u'klass': u'card card-widget span12',
-              u'oozieExpanded': False,
-              u'id': u'3f107997-04cc-8733-60a9-a4bb62cebffc',
-              u'size': 12,
-              }],
-          u'id': u'798dc16a-d366-6305-d2b3-2d5a6f6c4f4b',
-          u'columns': [],
-          }, {
-          u'enableOozieDropOnBefore': True,
-          u'enableOozieDropOnSide': True,
-          u'enableOozieDrop': False,
-          u'widgets': [{
-              u'status': u'',
-              u'logsURL': u'',
-              u'name': u'Hive',
-              u'widgetType': u'hive-document-widget',
-              u'oozieMovable': True,
-              u'ooziePropertiesExpanded': False,
-              u'externalIdUrl': u'',
-              u'properties': {},
-              u'isLoading': True,
-              u'offset': 0,
-              u'actionURL': u'',
-              u'progress': 0,
-              u'klass': u'card card-widget span12',
-              u'oozieExpanded': False,
-              u'id': u'0aec471d-2b7c-d93d-b22c-2110fd17ea2c',
-              u'size': 12,
-              }],
-          u'id': u'32e1ea1a-812b-6878-9719-ff7b8407bf46',
-          u'columns': [],
-          }, {
-          u'enableOozieDropOnBefore': True,
-          u'enableOozieDropOnSide': True,
-          u'enableOozieDrop': False,
-          u'widgets': [{
-              u'status': u'',
-              u'logsURL': u'',
-              u'name': u'End',
-              u'widgetType': u'end-widget',
-              u'oozieMovable': False,
-              u'ooziePropertiesExpanded': False,
-              u'externalIdUrl': u'',
-              u'properties': {},
-              u'isLoading': True,
-              u'offset': 0,
-              u'actionURL': u'',
-              u'progress': 0,
-              u'klass': u'card card-widget span12',
-              u'oozieExpanded': False,
-              u'id': u'33430f0f-ebfa-c3ec-f237-3e77efa03d0a',
-              u'size': 12,
-              }],
-          u'id': u'f2cf152d-8c82-2f4f-5d67-2e18c99e59c4',
-          u'columns': [],
-          }, {
-          u'enableOozieDropOnBefore': True,
-          u'enableOozieDropOnSide': True,
-          u'enableOozieDrop': False,
-          u'widgets': [{
-              u'status': u'',
-              u'logsURL': u'',
-              u'name': u'Kill',
-              u'widgetType': u'kill-widget',
-              u'oozieMovable': True,
-              u'ooziePropertiesExpanded': False,
-              u'externalIdUrl': u'',
-              u'properties': {},
-              u'isLoading': True,
-              u'offset': 0,
-              u'actionURL': u'',
-              u'progress': 0,
-              u'klass': u'card card-widget span12',
-              u'oozieExpanded': False,
-              u'id': u'17c9c895-5a16-7443-bb81-f34b30b21548',
-              u'size': 12,
-              }],
-          u'id': u'01afcf1b-fa7a-e093-b613-ce52c5531a04',
-          u'columns': [],
-          }],
-      u'oozieEndRow': {
-          u'enableOozieDropOnBefore': True,
-          u'enableOozieDropOnSide': True,
-          u'enableOozieDrop': False,
-          u'widgets': [{
-              u'status': u'',
-              u'logsURL': u'',
-              u'name': u'End',
-              u'widgetType': u'end-widget',
-              u'oozieMovable': False,
-              u'ooziePropertiesExpanded': False,
-              u'externalIdUrl': u'',
-              u'properties': {},
-              u'isLoading': True,
-              u'offset': 0,
-              u'actionURL': u'',
-              u'progress': 0,
-              u'klass': u'card card-widget span12',
-              u'oozieExpanded': False,
-              u'id': u'33430f0f-ebfa-c3ec-f237-3e77efa03d0a',
-              u'size': 12,
-              }],
-          u'id': u'f2cf152d-8c82-2f4f-5d67-2e18c99e59c4',
-          u'columns': [],
-          },
-      u'oozieKillRow': {
-          u'enableOozieDropOnBefore': True,
-          u'enableOozieDropOnSide': True,
-          u'enableOozieDrop': False,
-          u'widgets': [{
-              u'status': u'',
-              u'logsURL': u'',
-              u'name': u'Kill',
-              u'widgetType': u'kill-widget',
-              u'oozieMovable': True,
-              u'ooziePropertiesExpanded': False,
-              u'externalIdUrl': u'',
-              u'properties': {},
-              u'isLoading': True,
-              u'offset': 0,
-              u'actionURL': u'',
-              u'progress': 0,
-              u'klass': u'card card-widget span12',
-              u'oozieExpanded': False,
-              u'id': u'17c9c895-5a16-7443-bb81-f34b30b21548',
-              u'size': 12,
-              }],
-          u'id': u'01afcf1b-fa7a-e093-b613-ce52c5531a04',
-          u'columns': [],
-          },
-      u'enableOozieDropOnAfter': True,
-      u'oozieStartRow': {
-          u'enableOozieDropOnBefore': True,
-          u'enableOozieDropOnSide': True,
-          u'enableOozieDrop': False,
-          u'widgets': [{
-              u'status': u'',
-              u'logsURL': u'',
-              u'name': u'Start',
-              u'widgetType': u'start-widget',
-              u'oozieMovable': False,
-              u'ooziePropertiesExpanded': False,
-              u'externalIdUrl': u'',
-              u'properties': {},
-              u'isLoading': True,
-              u'offset': 0,
-              u'actionURL': u'',
-              u'progress': 0,
-              u'klass': u'card card-widget span12',
-              u'oozieExpanded': False,
-              u'id': u'3f107997-04cc-8733-60a9-a4bb62cebffc',
-              u'size': 12,
-              }],
-          u'id': u'798dc16a-d366-6305-d2b3-2d5a6f6c4f4b',
-          u'columns': [],
-          },
-      u'klass': u'card card-home card-column span12',
-      u'enableOozieDropOnBefore': True,
-      u'drops': [u'temp'],
-      u'id': u'672ff75a-d841-72c3-c616-c9d45ec97649',
-      u'size': 12,
-      }]}
-    )
+      }
+    })
 
     workflow_doc = Document2.objects.create(name=name, type='oozie-workflow2', owner=user, data=data, is_managed=managed)
     Document.objects.link(workflow_doc, owner=workflow_doc.owner, name=workflow_doc.name, description=workflow_doc.description, extra='workflow2')

+ 1 - 0
apps/oozie/src/oozie/static/oozie/js/workflow-editor.ko.js

@@ -1257,6 +1257,7 @@ var WorkflowEditorViewModel = function (layout_json, workflow_json, credentials_
   self.draggableSparkAction = ko.observable(bareWidgetBuilder("Spark", "spark-widget"));
   self.draggableGenericAction = ko.observable(bareWidgetBuilder("Generic", "generic-widget"));
   self.draggableHiveDocumentAction = ko.observable(bareWidgetBuilder("Hive", "hive-document-widget"));
+  self.draggableJavaDocumentAction = ko.observable(bareWidgetBuilder("Java", "java-document-widget"));  
   self.draggableKillNode = ko.observable(bareWidgetBuilder("Kill", "kill-widget"));
 };
 

+ 74 - 1
apps/oozie/src/oozie/templates/editor2/common_workflow.mako

@@ -165,7 +165,7 @@
       <img src="${ static('oozie/art/icon_pig_48.png') }" class="widget-icon">
       <!-- /ko -->
 
-      <!-- ko if: widgetType() == 'java-widget' -->
+      <!-- ko if: widgetType() == 'java-widget' || widgetType() == 'java-document-widget' -->
       <a class="widget-icon"><i class="fa fa-file-code-o"></i></a>
       <!-- /ko -->
 
@@ -1016,6 +1016,79 @@
 </script>
 
 
+<script type="text/html" id="java-document-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="padding: 10px">
+
+    <div data-bind="visible: ! $root.isEditing()">
+      <span data-bind="template: { name: 'logs-icon' }"></span>
+      <!-- ko if: $root.getHiveQueryById(properties.uuid()) -->
+      <!-- ko with: $root.getHiveQueryById(properties.uuid()) -->
+        <a data-bind="attr: { href: absoluteUrl() }" target="_blank"><span data-bind='text: name'></span></a>
+        <br/>
+        <span data-bind='text: description' class="muted"></span>
+      <!-- /ko -->
+      <!-- /ko -->
+    </div>
+
+    <div data-bind="visible: $root.isEditing">
+      <div data-bind="visible: ! $parent.ooziePropertiesExpanded()" class="nowrap">
+        <!-- ko if: $root.getHiveQueryById(properties.uuid()) -->
+        <!-- ko with: $root.getHiveQueryById(properties.uuid()) -->
+          <select data-bind="options: $root.hiveQueries, optionsText: 'name', optionsValue: 'uuid', value: $parent.properties.uuid, select2Version4:{ placeholder: '${ _ko('Hive query name...')}'}"></select>
+          <a href="#" data-bind="attr: { href: absoluteUrl() }" target="_blank" title="${ _('Open') }">
+            <i class="fa fa-external-link-square"></i>
+          </a>
+          <div data-bind='text: description' style="padding: 3px; margin-top: 2px" class="muted"></div>
+        <!-- /ko -->
+        <!-- /ko -->
+
+        <div class="row-fluid">
+          <div class="span6" data-bind="template: { name: 'common-properties-parameters' }"></div>
+          <div class="span6" data-bind="template: { name: 'common-properties-files' }"></div>
+        </div>
+      </div>
+    </div>
+
+    <div data-bind="visible: $parent.ooziePropertiesExpanded">
+      <ul class="nav nav-tabs">
+        <li class="active"><a data-bind="attr: { href: '#properties-' + id()}" data-toggle="tab">${ _('Properties') }</a></li>
+        <li><a data-bind="attr: { href: '#sla-' + id()}" href="#sla" data-toggle="tab">${ _('SLA') }</a></li>
+        <li><a data-bind="attr: { href: '#credentials-' + id()}" data-toggle="tab">${ _('Credentials') }</a></li>
+        <li><a data-bind="attr: { href: '#transitions-' + id()}" data-toggle="tab">${ _('Transitions') }</a></li>
+      </ul>
+      <div class="tab-content">
+        <div class="tab-pane active" data-bind="attr: { id: 'properties-' + id() }">
+          <span data-bind="text: $root.workflow_properties.jdbc_url.label"></span>
+          <input type="text" data-bind="value: properties.jdbc_url, attr: { placeholder: $root.workflow_properties.jdbc_url.help_text }" />
+          <br/>
+          <span data-bind="text: $root.workflow_properties.password.label"></span>
+          <input type="text" data-bind="value: properties.password, attr: { placeholder: $root.workflow_properties.password.help_text }" />
+          <br/>
+          <span data-bind="template: { name: 'common-action-properties' }"></span>
+          <br/>
+          <br/>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'sla-' + id() }">
+          <span data-bind="template: { name: 'common-action-sla' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'credentials-' + id() }">
+          <span data-bind="template: { name: 'common-action-credentials' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'transitions-' + id() }">
+          <span data-bind="template: { name: 'common-action-transition' }"></span>
+        </div>
+      </div>
+    </div>
+  </div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
 <script type="text/html" id="java-widget">
   <!-- ko if: $root.workflow.getNodeById(id()) -->
   <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="padding: 10px">

+ 7 - 0
apps/oozie/src/oozie/templates/editor2/workflow_editor.mako

@@ -115,6 +115,13 @@ ${ layout.menubar(section='workflows', is_editor=True, pullright=buttons) }
          <a class="draggable-icon"><img src="${ static('oozie/art/icon_beeswax_48.png') }" class="app-icon"><sup style="color: #338bb8; margin-left: -4px; top: -14px; font-size: 12px">2</sup></a>
     </div>
 
+    <div data-bind="css: { 'draggable-widget': true },
+                    draggable: {data: draggableJavaDocumentAction(), isEnabled: true,
+                    options: {'refreshPositions': true, 'stop': function(){ $root.isDragging(false); }, 'start': function(event, ui){ $root.isDragging(true); $root.currentlyDraggedWidget(draggableJavaDocumentAction());}}}"
+         title="${_('Saved Java program')}" rel="tooltip" data-placement="top">
+         <a class="draggable-icon"><i class="fa fa-file-code-o"></i></a>
+    </div>
+
     <div class="toolbar-label">${ _('ACTIONS') }</div>
 
     <div data-bind="css: { 'draggable-widget': true },

+ 1 - 1
apps/oozie/src/oozie/views/dashboard.py

@@ -395,7 +395,7 @@ def list_oozie_workflow(request, job_id):
     'parameters': dict((var, val) for var, val in parameters.iteritems() if var not in ParameterForm.NON_PARAMETERS and var != 'oozie.use.system.libpath' or var == 'oozie.wf.application.path'),
     'has_job_edition_permission': has_job_edition_permission,
     'workflow_graph': workflow_graph,
-    'layout_json': json.dumps(workflow_data['layout'], cls=JSONEncoderForHTML) if workflow_data else '',
+    'layout_json': json.dumps(workflow_data['layout'], cls=JSONEncoderForHTML) if workflow_data.get('layout') else '',
     'workflow_json': json.dumps(workflow_data['workflow'], cls=JSONEncoderForHTML) if workflow_data else '',
     'credentials_json': json.dumps(credentials.credentials.keys(), cls=JSONEncoderForHTML) if credentials else '',
     'workflow_properties_json': json.dumps(WORKFLOW_NODE_PROPERTIES, cls=JSONEncoderForHTML),

+ 7 - 0
desktop/conf.dist/hue.ini

@@ -599,6 +599,9 @@
   ## Flag to enable the bulk submission of queries as a background task through Oozie.
   # enable_batch_execute=true
 
+  ## Flag to enable the Java document in editor and workflow.
+  # enable_java_document=true
+
   ## Base URL to Remote GitHub Server
   # github_remote_url=https://github.com
 
@@ -683,6 +686,10 @@
       ## Name of the collection handler
       # options='{"collection": "default"}'
 
+    [[[java]]]
+      name=Java
+      interface=oozie 
+
     # [[[mysql]]]
     #   name=MySql JDBC
     #   interface=jdbc

+ 7 - 0
desktop/conf/pseudo-distributed.ini.tmpl

@@ -607,6 +607,9 @@
   ## Flag to enable the bulk submission of queries as a background task through Oozie.
   # enable_batch_execute=true
 
+  ## Flag to enable the Java document in editor and workflow.
+  # enable_java_document=true
+
   ## Base URL to Remote GitHub Server
   # github_remote_url=https://github.com
 
@@ -691,6 +694,10 @@
       ## Name of the collection handler
       # options='{"collection": "default"}'
 
+    [[[java]]]
+      name=Java
+      interface=oozie 
+
     # [[[mysql]]]
     #   name=MySql JDBC
     #   interface=jdbc

+ 1 - 1
desktop/core/src/desktop/views.py

@@ -410,7 +410,7 @@ def commonheader(title, section, user, padding="90px", skip_topbar=False, skip_i
       'attribution': desktop.conf.LEAFLET_TILE_LAYER_ATTRIBUTION.get()
     },
     'is_demo': desktop.conf.DEMO_ENABLED.get(),
-    'is_ldap_setup': 'desktop.auth.backend.LdapBackend' in desktop.conf.AUTH.BACKEND.get()
+    'is_ldap_setup': 'desktop.auth.backend.LdapBackend' in desktop.conf.AUTH.BACKEND.get(),
   })
 
 def commonshare():

+ 11 - 0
desktop/libs/notebook/src/notebook/conf.py

@@ -109,6 +109,13 @@ ENABLE_BATCH_EXECUTE = Config(
   dynamic_default=is_oozie_enabled
 )
 
+ENABLE_JAVA_DOCUMENT = Config(
+  key="enable_java_document",
+  help=_t("Flag to enable the Java document in editor and workflow."),
+  type=bool,
+  dynamic_default=is_oozie_enabled
+)
+
 
 GITHUB_REMOTE_URL = Config(
     key="github_remote_url",
@@ -168,6 +175,10 @@ def _default_interpreters():
       ('solr', {
           'name': 'Solr SQL', 'interface': 'solr', 'options': {}
       }),
+      ('java', {
+          'name': 'Java', 'interface': 'oozie', 'options': {}
+      })
+      ,
       ('text', {
           'name': 'Text', 'interface': 'text', 'options': {}
       }),

+ 6 - 1
desktop/libs/notebook/src/notebook/static/notebook/js/notebook.ko.js

@@ -177,7 +177,12 @@
       properties['files'] = [];
     }
 
-    if (snippetType == 'jar') {
+    if (snippetType == 'java') {
+      properties['archives'] = [];
+      properties['files'] = [];
+    }
+
+    if (snippetType == 'jar' || snippetType == 'java') {
       properties['app_jar'] = '';
       properties['class'] = '';
       properties['arguments'] = [];

+ 7 - 4
desktop/libs/notebook/src/notebook/templates/editor_components.mako

@@ -1395,10 +1395,10 @@ ${ hueIcons.symbols() }
                 <!-- ko template: { if: $root.editorMode(), name: 'editor-snippet-header' } --><!-- /ko -->
                 <!-- ko template: { if: ! $root.editorMode(), name: 'notebook-snippet-header' } --><!-- /ko -->
               </h2>
-              <!-- ko template: { if: ['text', 'jar', 'py', 'markdown'].indexOf(type()) == -1, name: 'code-editor-snippet-body' } --><!-- /ko -->
+              <!-- ko template: { if: ['text', 'jar', 'java', 'py', 'markdown'].indexOf(type()) == -1, name: 'code-editor-snippet-body' } --><!-- /ko -->
               <!-- ko template: { if: type() == 'text', name: 'text-snippet-body' } --><!-- /ko -->
               <!-- ko template: { if: type() == 'markdown', name: 'markdown-snippet-body' } --><!-- /ko -->
-              <!-- ko template: { if: type() == 'jar' || type() == 'py', name: 'executable-snippet-body' } --><!-- /ko -->
+              <!-- ko template: { if: ['java', 'jar', 'py'].indexOf(type()) != -1, name: 'executable-snippet-body' } --><!-- /ko -->
             </div>
             <div style="position: absolute; top:25px; margin-left:35px; width: calc(100% - 35px)" data-bind="style: { 'z-index': 400 - $index() }">
               <!-- ko template: 'snippet-settings' --><!-- /ko -->
@@ -1412,7 +1412,7 @@ ${ hueIcons.symbols() }
           <!-- /ko -->
           <!-- ko ifnot: $root.editorMode() -->
           <!-- ko template: 'snippet-log' --><!-- /ko -->
-          <!-- ko template: { if: ['text', 'jar', 'py', 'markdown'].indexOf(type()) == -1, name: 'snippet-results' } --><!-- /ko -->
+          <!-- ko template: { if: ['text', 'jar', 'java', 'py', 'markdown'].indexOf(type()) == -1, name: 'snippet-results' } --><!-- /ko -->
           <!-- /ko -->
 
           <div class="clearfix"></div>
@@ -1757,7 +1757,7 @@ ${ hueIcons.symbols() }
 <script type="text/html" id="executable-snippet-body">
   <div style="padding:10px;">
     <form class="form-horizontal">
-      <!-- ko if: type() == 'jar' -->
+      <!-- ko if: type() == 'jar' || type() == 'java' -->
       <div class="control-group">
         <label class="control-label">${_('Path')}</label>
         <div class="controls">
@@ -2709,6 +2709,9 @@ ${ hueIcons.symbols() }
           snippetIcon: 'fa-database',
           sqlDialect: true
         },
+        java : {
+          snippetIcon: 'fa-file-archive-o '
+        },
         py : {
           snippetIcon: 'fa-file-code-o'
         },