• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 package com.kh.scan.dialog;
16 
17 import com.intellij.notification.NotificationType;
18 import com.intellij.openapi.diagnostic.Logger;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.openapi.ui.ValidationInfo;
21 import com.kh.scan.action.ScanDirAction;
22 import com.kh.scan.action.ScanResultDirAction;
23 import com.kh.scan.utils.FileInfo;
24 import com.kh.scan.utils.FileUtil;
25 import com.kh.scan.utils.GenNotification;
26 import org.apache.http.util.TextUtils;
27 import org.jetbrains.annotations.Nullable;
28 
29 import javax.swing.JButton;
30 import javax.swing.JComponent;
31 import javax.swing.JDialog;
32 import javax.swing.JPanel;
33 import javax.swing.JTextField;
34 import javax.swing.KeyStroke;
35 import java.awt.event.KeyEvent;
36 import java.awt.event.WindowAdapter;
37 import java.awt.event.WindowEvent;
38 import java.awt.event.WindowListener;
39 import java.io.BufferedReader;
40 import java.io.File;
41 import java.io.FileOutputStream;
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.io.InputStreamReader;
45 import java.util.ArrayList;
46 import java.util.List;
47 import java.util.concurrent.BlockingQueue;
48 import java.util.concurrent.LinkedBlockingQueue;
49 import java.util.concurrent.ThreadPoolExecutor;
50 import java.util.concurrent.TimeUnit;
51 
52 /**
53  * GenerateDialogPane生成工具主界面
54  *
55  * @author: zhaoxudong@kaihong.com
56  * @see: select generate dialog
57  * @version: v1.0.0
58  * @since 2022-10-14
59  */
60 public class ApiScanDialogPane extends JDialog {
61     private static final Logger LOG = Logger.getInstance(ApiScanDialogPane.class);
62 
63     private JPanel contentPane;
64     private JTextField scanDirPathTextField;
65     private JTextField outScanResultPathTextField;
66     private JButton selectScanPath;
67     private JButton outSelectPath;
68     private boolean generateSuccess = true;
69     private String sErrorMessage = "";
70     private String scanDir;
71     private String scanResultDir;
72     private final Project project;
73     private BlockingQueue blockingQueue = new LinkedBlockingQueue(100);
74     private ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 64, 60L,
75             TimeUnit.SECONDS, blockingQueue,
76             new ThreadPoolExecutor.AbortPolicy());
77 
78 
79     /**
80      * 构造函数
81      *
82      * @param project projectId
83      */
ApiScanDialogPane(Project project)84     public ApiScanDialogPane(Project project) {
85         this.project = project;
86         contentPane.registerKeyboardAction(actionEvent -> onCancel(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
87                 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
88         selectScanPath.addActionListener(new ScanDirAction(selectScanPath, scanDirPathTextField,
89                 outScanResultPathTextField));
90         outSelectPath.addActionListener(new ScanResultDirAction(outSelectPath, outScanResultPathTextField));
91     }
92 
93     @Override
addWindowListener(WindowListener windowListener)94     public synchronized void addWindowListener(WindowListener windowListener) {
95         super.addWindowListener(windowListener);
96         new WindowAdapter() {
97             /**
98              * close dialog
99              *
100              * @param windowEvent WindowEvent
101              */
102             @Override
103             public void windowClosing(WindowEvent windowEvent) {
104                 onCancel();
105             }
106         };
107     }
108 
109     /**
110      * 验证文本选择框是否空。是否替换已存在的内容
111      *
112      * @return ValidationInfo 返回不符要求的信息。
113      */
114     @Nullable
validationInfo()115     public ValidationInfo validationInfo() {
116         String scanResultDirPath = outScanResultPathTextField.getText();
117         String scanDirPath = scanDirPathTextField.getText();
118         boolean isEmptyFile = TextUtils.isEmpty(scanDirPath) || TextUtils.isEmpty(scanResultDirPath);
119 
120         ValidationInfo validationInfo = null;
121         if (isEmptyFile) {
122             String warnMsg = "扫描项目路径、结果输出路径不能为空";
123             warningMessage(warnMsg);
124             validationInfo = new ValidationInfo(warnMsg);
125             return validationInfo;
126         }
127 
128         File file = new File(scanResultDirPath + "/result.xlsx");
129         if (file.exists()) {
130             ConfirmDialog confirmDialog = new ConfirmDialog("是否替换已存在的扫描结果?");
131             if (!confirmDialog.showAndGet()) {
132                 validationInfo = new ValidationInfo(String.format("不替换现有扫描结果文件:%s", file));
133                 return validationInfo;
134             }
135         }
136         return validationInfo;
137     }
138 
onCancel()139     private void onCancel() {
140         dispose();
141     }
142 
warningMessage(String title)143     private void warningMessage(String title) {
144         String notiContent = "请选择扫描项目路径、结果输出路径";
145         GenNotification.notifyMessage(this.project, notiContent, title, NotificationType.WARNING);
146     }
147 
148     /**
149      * 获取指定输出目录下的文件列表
150      *
151      * @param outPath 输出目录
152      * @return 文件信息列表
153      */
getFileInfoList(File outPath)154     public List<FileInfo> getFileInfoList(File outPath) {
155         List<FileInfo> fileInfoList = new ArrayList<>();
156         File[] files = outPath.listFiles();
157             for (File file : files) {
158                 fileInfoList.add(new FileInfo(file));
159             }
160         return fileInfoList;
161     }
162 
163     /**
164      * 执行主程序入口
165      *
166      * @return 执行状态
167      */
runFun()168     public boolean runFun() {
169         GenNotification.notifyMessage(this.project, "", "正在生成", NotificationType.INFORMATION);
170         scanDir = scanDirPathTextField.getText();
171         scanResultDir = outScanResultPathTextField.getText();
172         String command = genCommand();
173 
174         File outPath = new File(outScanResultPathTextField.getText());
175         List<FileInfo> oldFileList = getFileInfoList(outPath);
176 
177         try {
178             if (!TextUtils.isEmpty(command) && callExtProcess(command)) {
179                 List<FileInfo> newFileList = getFileInfoList(outPath);
180                 newFileList.removeAll(oldFileList);
181                 if (generateSuccess) {
182                     GenNotification.notifyGenResult(project, newFileList, "Api Scan Successfully",
183                             NotificationType.INFORMATION);
184                     return true;
185                 } else {
186                     GenNotification.notifyMessage(project, sErrorMessage, "提示", NotificationType.ERROR);
187                     return false;
188                 }
189             }
190         } catch (IOException | InterruptedException ex) {
191             GenNotification.notifyMessage(project, scanDirPathTextField.getText(), "Command exec error",
192                     NotificationType.ERROR);
193             LOG.error(ex);
194         }
195         return false;
196     }
197 
198     /**
199      * 生成命令行指令
200      *
201      * @return 返回命令行执行内容
202      */
genCommand()203     private String genCommand() {
204         String sysName = System.getProperties().getProperty("os.name").toUpperCase();
205         String tmpDirFile = System.getProperty("java.io.tmpdir");
206         String execFn;
207         if (sysName.contains("WIN")) {
208             execFn = "cmds/win/search-win.exe";
209             tmpDirFile += "search-win.exe";
210         } else if (sysName.contains("LINUX")) {
211             execFn = "cmds/linux/search-linux";
212             tmpDirFile += "/search-linux";
213         } else {
214             execFn = "cmds/mac/search-macos";
215             tmpDirFile += "/search-macos";
216         }
217         File file = new File(tmpDirFile);
218         try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(execFn)) {
219             if (inputStream == null) {
220                 throw new IOException("exec File InputStream is Null");
221             }
222             byte[] bs = inputStream.readAllBytes();
223             writeTmpFile(tmpDirFile, bs);
224             if (sysName.contains("LINUX") || sysName.contains("MAC OS")) {
225                 executable(tmpDirFile);
226             }
227         } catch (IOException | InterruptedException e) {
228             GenNotification.notifyMessage(this.project, e.getMessage(), "Can not Find File:" + execFn,
229                     NotificationType.ERROR);
230             LOG.error(e);
231 
232             return "";
233         }
234         String command = file.toString();
235         command = command + " -d " + scanDir + " -o " + scanResultDir;
236         return command;
237     }
238 
callExtProcess(String command)239     private boolean callExtProcess(String command) throws IOException, InterruptedException {
240         if (TextUtils.isEmpty(command)) {
241             GenNotification.notifyMessage(this.project, "执行命令文件为空", "空命令行提示", NotificationType.ERROR);
242             return false;
243         }
244         String tmpDirFile = System.getProperty("java.io.tmpdir");
245         Process process = Runtime.getRuntime().exec(command, null, new File(tmpDirFile));
246         threadPool.execute(new BlockThread(process));
247         StreamConsumer outputConsumer = new StreamConsumer(process.getInputStream());
248         outputConsumer.start();
249         outputConsumer.join();
250         process.destroy();
251         return true;
252     }
253 
254     /**
255      * 赋值可执行文件权限。
256      *
257      * @param execFn 可执行命令
258      * @throws IOException          打开文件异常
259      * @throws InterruptedException 中断异常
260      */
executable(String execFn)261     private void executable(String execFn) throws IOException, InterruptedException {
262         callExtProcess("chmod a+x " + execFn);
263     }
264 
265     /**
266      * 拷贝可执行文件到临时文件夹
267      *
268      * @param path 目标文件路径
269      * @param bs   字节内容
270      * @throws IOException exception
271      */
writeTmpFile(String path, byte[] bs)272     private void writeTmpFile(String path, byte[] bs) throws IOException {
273         File file = new File(path);
274         FileOutputStream fw = null;
275         if (!file.exists()) {
276             boolean isNewFile = file.createNewFile();
277             if (!isNewFile) {
278                 LOG.info("writeTmpFile createNewFile error");
279             }
280         }
281         try {
282             fw = new FileOutputStream(file);
283             fw.write(bs, 0, bs.length);
284         } catch (IOException e) {
285             // 处理可能发生的IOException
286             LOG.error("Error reading from process streams", e);
287         } finally {
288             try {
289                 fw.close();
290             } catch (IOException e) {
291                 LOG.error("Error closing stdInput", e);
292             }
293         }
294     }
295 
296     /**
297      * 获取生成成功结果文件。
298      *
299      * @param process 进程ID
300      */
genResultLog(Process process)301     private void genResultLog(Process process) {
302         BufferedReader stdInput = null;
303         BufferedReader stdError = null;
304         try {
305             stdInput = new BufferedReader(new InputStreamReader(process.getInputStream(),
306                 StandardCharsets.UTF_8));
307             stdError = new BufferedReader(new InputStreamReader(process.getErrorStream(),
308                 StandardCharsets.UTF_8));
309             String sErr;
310             String sOut;
311             sErr = getErrorResult(stdError);
312             if (TextUtils.isEmpty(sErr)) {
313                 sOut = genInputLog(stdInput);
314                 if (!generateIsSuccess(sOut)) {
315                     sErrorMessage = sOut;
316                 }
317             } else {
318                 generateSuccess = false;
319                 sErrorMessage = sErr;
320             }
321         } catch (IOException e) {
322             // Handle exception
323             LOG.error(e);
324         } finally {
325             // Close resources in finally block to ensure they are closed even if an exception occurs
326             try {
327                 stdInput.close();
328             } catch (IOException e) {
329                 LOG.error(e);
330             }
331             try {
332                 stdError.close();
333             } catch (IOException e) {
334                 LOG.error(e);
335             }
336         }
337     }
338 
339     /**
340      * 获取生成失败结果文件。
341      *
342      * @param stdError error buff
343      * @return ErrorResult
344      */
getErrorResult(BufferedReader stdError)345     private String getErrorResult(BufferedReader stdError) {
346         StringBuilder sErr = new StringBuilder();
347         while (true) {
348             String sTmp;
349             try {
350                 if ((sTmp = stdError.readLine()) == null) {
351                     break;
352                 }
353                 sErr.append(sTmp).append(FileUtil.getNewline());
354             } catch (IOException ioException) {
355                 LOG.error(" genResultLog stdInput error" + ioException);
356             }
357         }
358         return sErr.toString();
359     }
360 
generateIsSuccess(String sOut)361     private boolean generateIsSuccess(String sOut) {
362         generateSuccess = sOut.contains("success") || TextUtils.isEmpty(sOut);
363         return generateSuccess;
364     }
365 
366     /**
367      * 获取生成文本内容。
368      *
369      * @param stdInput input buff
370      * @return 返回当前输入框内容
371      */
genInputLog(BufferedReader stdInput)372     private String genInputLog(BufferedReader stdInput) {
373         StringBuilder sOut = new StringBuilder();
374         while (true) {
375             String sTmp;
376             try {
377                 if ((sTmp = stdInput.readLine()) == null) {
378                     break;
379                 }
380                 sOut.append(sTmp).append(FileUtil.getNewline());
381             } catch (IOException ioException) {
382                 LOG.error(" genResultLog stdInput error" + ioException);
383             }
384         }
385         return sOut.toString();
386     }
387 
388     static class StreamConsumer extends Thread {
389         InputStream is;
390 
StreamConsumer(InputStream is)391         StreamConsumer(InputStream is) {
392             super.setName("StreamConsumer");
393             this.is = is;
394         }
395 
396         @Override
run()397         public void run() {
398             InputStreamReader isr5 = null;
399             BufferedReader br5 = null;
400             try {
401                 isr5 = new InputStreamReader(is, StandardCharsets.UTF_8);
402                 br5 = new BufferedReader(isr5);
403                 String line;
404                 while ((line = br5.readLine()) != null) {
405                     LOG.info(line);
406                 }
407             } catch (IOException ioException) {
408                 LOG.error("StreamConsumer io error" + ioException);
409             } finally {
410                 // 确保BufferedReader5 br和InputStreamReader isr5被关闭
411                 try {
412                     br5.close();
413                 } catch (IOException e) {
414                     LOG.error(e);
415                 }
416                 try {
417                     isr5.close();
418                 } catch (IOException e) {
419                     LOG.error(e);
420                 }
421             }
422         }
423     }
424 
getContentPanel()425     JPanel getContentPanel() {
426         return contentPane;
427     }
428 
429     class BlockThread extends Thread {
430         Process process;
431 
BlockThread(Process process)432         BlockThread(Process process) {
433             super.setName("BlockThread");
434             this.process = process;
435         }
436 
437         @Override
run()438         public void run() {
439             BufferedReader br2 = null;
440             try {
441                 br2 = new BufferedReader(new InputStreamReader(process.getInputStream(),
442                     StandardCharsets.UTF_8));
443                 genResultLog(process);
444                 while (br2.readLine() != null) {
445                     LOG.info(" callExtProcess ");
446                 }
447             } catch (IOException ioException) {
448                 LOG.error(" callExtProcess error" + ioException);
449             } finally {
450                 // 确保BufferedReader br2被关闭
451                 try {
452                     br2.close();
453                 } catch (IOException e) {
454                     // 处理关闭BufferedReader时的异常
455                     LOG.error(e);
456                 }
457             }
458         }
459     }
460 }
461