Add Dump Job (#148)

* Add Dump Job

* update comment and port

* update elasticjob version
diff --git a/pom.xml b/pom.xml
index 4d0ae98..187660d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -36,7 +36,7 @@
     </modules>
     
     <properties>
-        <elasticjob.version>3.0.2</elasticjob.version>
+        <elasticjob.version>3.1.0-SNAPSHOT</elasticjob.version>
         <java.version>1.8</java.version>
         <springframework.version>4.3.24.RELEASE</springframework.version>
         <spring-boot.version>1.5.21.RELEASE</spring-boot.version>
diff --git a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-backend/src/main/java/org/apache/shardingsphere/elasticjob/lite/ui/web/controller/ServerOperationController.java b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-backend/src/main/java/org/apache/shardingsphere/elasticjob/lite/ui/web/controller/ServerOperationController.java
index bad4727..6301c53 100644
--- a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-backend/src/main/java/org/apache/shardingsphere/elasticjob/lite/ui/web/controller/ServerOperationController.java
+++ b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-backend/src/main/java/org/apache/shardingsphere/elasticjob/lite/ui/web/controller/ServerOperationController.java
@@ -31,6 +31,7 @@
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.io.IOException;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Objects;
@@ -150,7 +151,26 @@
         jobAPIService.getJobOperatorAPI().enable(jobName, serverIp);
         return ResponseResultUtil.build(Boolean.TRUE);
     }
-    
+
+    /**
+     * Enable server job.
+     *
+     * @param serverIp server IP address
+     * @param jobName job name
+     */
+    @PostMapping("/{serverIp}/{dumpPort}/jobs/{jobName}/dump")
+    public ResponseResult<String> dumpJob(@PathVariable("serverIp") final String serverIp,
+                                           @PathVariable("dumpPort") final int dumpPort,
+                                           @PathVariable("jobName") final String jobName) {
+        String result;
+        try {
+            result = jobAPIService.getJobOperatorAPI().dump(jobName, serverIp, dumpPort);
+        } catch (IOException e) {
+            result = e.getMessage();
+        }
+        return ResponseResultUtil.build(result);
+    }
+
     /**
      * Shutdown server job.
      *
diff --git a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/lang/en-US.js b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/lang/en-US.js
index bd4472d..b868c6d 100644
--- a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/lang/en-US.js
+++ b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/lang/en-US.js
@@ -249,8 +249,17 @@
       disable: 'Disable',
       enable: 'Enable',
       shutdown: 'Shutdown',
+      dump: 'Dump',
       remove: 'Remove'
     },
+    dumpText: {
+      title: 'Dump Job',
+      jobName: 'Job Name',
+      dumpPort: 'Dump Port',
+      dumpBtn: 'Dump',
+      cancel: 'Cancel',
+      copy: 'Copy'
+    },
     actionConfirm: {
       shutdown: 'Are you sure to shutdown the server?'
     }
diff --git a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/lang/zh-CN.js b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/lang/zh-CN.js
index 25bba01..2b8c783 100644
--- a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/lang/zh-CN.js
+++ b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/lang/zh-CN.js
@@ -249,8 +249,17 @@
       disable: '失效',
       enable: '生效',
       shutdown: '终止',
+      dump: '导出',
       remove: '删除'
     },
+    dumpText: {
+      title: '导出作业',
+      jobName: '作业名称',
+      dumpPort: '导出端口',
+      dumpBtn: '导出',
+      cancel: '取消',
+      copy: '复制'
+    },
     actionConfirm: {
       shutdown: '您确定要下线服务器吗?'
     }
diff --git a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/operation-servers-detail/module/operationServersDetail.vue b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/operation-servers-detail/module/operationServersDetail.vue
index ff2f455..fd5d05f 100644
--- a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/operation-servers-detail/module/operationServersDetail.vue
+++ b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/operation-servers-detail/module/operationServersDetail.vue
@@ -17,6 +17,53 @@
 
 <template>
   <el-row class="box-card">
+
+    <div>
+      <el-dialog
+        :title="dumpInfo.title"
+        :visible.sync="dumpConfigDialogVisible"
+        :close-on-click-modal="true"
+        :modal="true"
+        :show-close="true"
+        :center="true"
+        style="text-align:center">
+        <span style="padding-right: 5vh;">
+          <span style="font-weight: bolder;">{{ $t("operationServers.dumpText.jobName") }}:</span>
+          <span >{{ jobName }}</span>
+        </span>
+        <span >
+          <span style="font-weight: bolder;">{{ $t("operationServers.dumpText.dumpPort") }}:</span>
+          <el-input-number
+            ref="dumPort"
+            v-model="dumpPort"
+            :min="1025"
+            :max="65535"
+            autocomplete="off"
+          />
+          <input ref="jobName" type="hidden">
+        </span>
+        <span slot="footer" class="dialog-footer">
+          <el-button @click="dumpConfigDialogVisible = false">{{ $t('operationServers.dumpText.cancel') }}</el-button>
+          <el-button type="primary" @click="handleDump">{{ $t('operationServers.dumpText.dumpBtn') }}</el-button>
+        </span>
+      </el-dialog>
+
+      <!--dump dialog-->
+      <el-dialog
+        :title="dumpInfo.title"
+        :visible.sync="dialogVisible"
+        :close-on-click-modal="true"
+        :modal="true"
+        :show-close="true"
+        :center="true"
+        style="height: 80%;">
+        <span style="white-space: pre-wrap;" class="dumpContent">{{ dumpContent }}</span>
+        <span slot="footer" class="dialog-footer">
+          <el-button type="primary" @click="copy">{{ $t('operationServers.dumpText.copy') }}</el-button>
+        </span>
+      </el-dialog>
+    </div>
+
     <el-form :model="searchForm" class="demo-form-inline">
       <el-form-item>
         <el-col :span="4">
@@ -135,6 +182,13 @@
                 plain
                 @click="handleShutdown(scope.row)">{{ $t("operationServers.actionText.shutdown") }}</el-button>
               <el-button
+                v-if="scope.row.instanceCount"
+                :disabled="isGuest"
+                size="mini"
+                type="danger"
+                plain
+                @click="dumpDialog(scope.row)">{{ $t("operationServers.actionText.dump") }}</el-button>
+              <el-button
                 v-if="!scope.row.instanceCount"
                 size="mini"
                 type="danger"
@@ -165,6 +219,11 @@
   name: 'OperationServers',
   data() {
     return {
+      dumpConfigDialogVisible: false,
+      dialogVisible: false,
+      jobName: '',
+      dumpPort: 9888,
+      dumpContent: '',
       serverIp: '',
       columnJobName: {
         label: this.$t('operationServers').labelInfo.jobName,
@@ -178,6 +237,14 @@
         label: this.$t('operationServers').labelInfo.status,
         prop: 'status'
       },
+      dumpInfo: {
+        title: this.$t('operationServers').dumpText.title,
+        jobName: this.$t('operationServers').dumpText.jobName,
+        dumpPort: this.$t('operationServers').dumpText.dumpPort,
+        dumpBtn: this.$t('operationServers').dumpText.dumpBtn,
+        cancel: this.$t('operationServers').dumpText.cancel,
+        copy: this.$t('operationServers').dumpText.copy
+      },
       statusColor: {
         OK: 'success',
         DISABLED: 'warning',
@@ -275,6 +342,25 @@
         this.search()
       })
     },
+    dumpDialog(row) {
+      this.dumpPort = 9888
+      this.dumpConfigDialogVisible = true
+      this.jobName = row.jobName
+    },
+    handleDump() {
+      const params = {
+        serverIp: this.serverIp,
+        dumpPort: this.$refs.dumPort.value,
+        jobName: this.jobName
+      }
+      API.dumpServerJob(params).then(res => {
+        this.dumpConfigDialogVisible = false
+        this.dialogVisible = true
+        // alert(res.model)
+        // this.dumpContent=res.model.replace(/\n/g, '<br>')
+        this.dumpContent = res.model
+      })
+    },
     handleRemove(row) {
       const params = {
         serverIp: this.serverIp,
@@ -291,6 +377,29 @@
     },
     search() {
       this.getJobs()
+    },
+
+    copy() {
+      const text = document.querySelector('.dumpContent').innerText
+      const target = document.createElement('textarea')
+      target.id = 'creatDom'
+      target.value = text
+      document.body.appendChild(target)
+      target.select()
+      try {
+        document.execCommand('Copy')
+        console.log('copy success.')
+        const creatDom = document.getElementById('creatDom')
+        creatDom.parentNode.removeChild(creatDom)
+        this.$notify({
+          title: this.$t('common').notify.title,
+          message: this.$t('common').notify.actionSucMessage,
+          type: 'success'
+        })
+      } catch (e) {
+        console.log('copy failure.')
+      }
+      this.dialogVisible = false
     }
   }
 }
diff --git a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/operation-servers/api.js b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/operation-servers/api.js
index 4ca2924..3f2ef60 100644
--- a/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/operation-servers/api.js
+++ b/shardingsphere-elasticjob-lite-ui/shardingsphere-elasticjob-lite-ui-frontend/src/views/operation-servers/api.js
@@ -30,6 +30,7 @@
   disableServerJob: (params) => API.post('/api/servers/' + params.serverIp + '/jobs/' + params.jobName + '/disable'),
   enableServerJob: (params) => API.post('/api/servers/' + params.serverIp + '/jobs/' + params.jobName + '/enable'),
   shutdownServerJob: (params) => API.post('/api/servers/' + params.serverIp + '/jobs/' + params.jobName + '/shutdown'),
+  dumpServerJob: (params) => API.post('/api/servers/' + params.serverIp + '/' + params.dumpPort + '/jobs/' + params.jobName + '/dump'),
   removeServerJob: (params) => API.delete('/api/servers/' + params.serverIp + '/jobs/' + params.jobName + '')
 
 }