一、需求概述
因安全管控,对项目开发过程中引入的jar需要做版本源码扫描,故开发了此工具,实现根据pom文件自动抓取jar源码。
二、实现思路
三、实现过程
1. 通过UI界面提交pom依赖
2. 将pom依赖填充模板
模板内容为pom.tpl
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.fly</groupId>
<artifactId>example</artifactId>
<version>1.0.0</version>
<name>example</name>
<url>http://maven.apache.org</url>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<!-- 依赖包定义请更新<dependencies></dependencies>内容 -->
<dependencies>
%s
</dependencies>
<!-- 阿里云maven仓库 -->
<repositories>
<repository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public/</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<!-- 将项目依赖包复制到<outputDirectory>/lib目录下 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.2</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
3. 收集Jar和源码
将模板内容填充参数写入文件后执行mvn clean package -f {pom文件路径}
,通过maven-dependency-plugin
实现了Jar收集到lib目录 ,执行mvn dependency:sources -DdownloadSources=true -f {pom文件路径}
实现jar源码下载,并拷贝到Jar所在lib包。
SimpleSourceUI.java
import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.Frame;
import java.awt.HeadlessException;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import com.fly.source.utils.Executor;
import com.fly.source.utils.MvnUtils;
import lombok.extern.slf4j.Slf4j;
/**
*
* 工具UI简化版本
*
* @author 00fly
* @version [版本号, 2023年3月3日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
@Slf4j
public class SimpleSourceUI extends JFrame
{
private static final long serialVersionUID = -6346496604061152215L;
JFrame frame = this;
public SimpleSourceUI()
throws HeadlessException
{
initComponent();
this.setVisible(true);
this.setResizable(true);
this.setAlwaysOnTop(true);
this.setTitle("POM源码工具 V1.0");
this.setBounds(400, 200, 1200, 550);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
try
{
// 设定用户界面的外观
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
SwingUtilities.updateComponentTreeUI(this);
}
catch (Exception e)
{
}
}
/**
* 组件初始化
*
* @see [类、类#方法、类#成员]
*/
private void initComponent()
{
// 加载图标
URL imgURL = getClass().getResource("/img/icon.gif");
if (imgURL != null)
{
setIconImage(getToolkit().createImage(imgURL));
}
JTextArea textArea = new JTextArea();
textArea.setToolTipText("请输入dependencies节点内容, 例如:<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.15.0</version></dependency>");
textArea.setText("<!-- 请替换下面内容 -->\n<dependency>\n <groupId>commons-io</groupId>\n <artifactId>commons-io</artifactId>\n <version>2.15.0</version>\n</dependency>");
// JTextArea不自带滚动条,因此就需要把文本区放到一个滚动窗格中
JScrollPane scroll = new JScrollPane(textArea);
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
JButton button = new JButton(" 获 取 源 码 包 ");
this.add(scroll, BorderLayout.CENTER);
this.add(button, BorderLayout.SOUTH);
button.addMouseListener(new MouseAdapter()
{
@Override
public void mouseClicked(MouseEvent event)
{
try
{
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
String dependencies = textArea.getText();
if (StringUtils.isBlank(dependencies))
{
throw new Exception("dependencies节点内容不合法,请修改");
}
try (InputStream is = QuickSourceUI.class.getResourceAsStream("/pom.tpl"))
{
String pomTemplate = IOUtils.toString(is, StandardCharsets.UTF_8);
String pom = String.format(pomTemplate, dependencies);
File dest = new File("upload/pom.xml");
FileUtils.writeStringToFile(dest, pom, StandardCharsets.UTF_8, false);
// 运行package命令,通过pom文件中的插件maven-dependency-plugin收集jar到lib
String pomPath = dest.getCanonicalPath();
log.info("pom.xml 文件路径:{}", pomPath);
Executor.execute("mvn clean package -f " + pomPath).forEach(log::info);
// 收集sources
MvnUtils.copyResource(pomPath);
setExtendedState(Frame.ICONIFIED);
}
}
catch (Exception e)
{
JOptionPane.showMessageDialog(frame, e.getMessage(), "系統异常", JOptionPane.ERROR_MESSAGE);
}
finally
{
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
});
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> new SimpleSourceUI());
}
}
4、运行结果
5、针对完整的有依赖关系的pom文件
可直接选择pom文件
核心代码如下:
QuickSourceUI.java
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.filechooser.FileFilter;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import com.fly.source.utils.Executor;
import com.fly.source.utils.MvnUtils;
import lombok.extern.slf4j.Slf4j;
/*****
* 界面操作展示类
*/
@Slf4j
public class QuickSourceUI extends JFrame
{
private static final long serialVersionUID = -9154321945329564644L;
// 界面组件
JPanel panel = new JPanel();
JTextArea textArea = new JTextArea();
JTextField fileText = new JTextField(null, 40);
JButton fileDirBrowse = new JButton("请选择");
JButton runButton = new JButton(" 获 取 源 码 包 ");
// 构造函数
public QuickSourceUI()
{
// 加载图标
URL imgURL = getClass().getResource("/img/icon.gif");
if (imgURL != null)
{
setIconImage(getToolkit().createImage(imgURL));
}
setTitle("POM源码工具 V1.0");
setSize(1000, 800);
Dimension screenSize = getToolkit().getScreenSize();
Dimension frameSize = this.getSize();
frameSize.height = Math.min(screenSize.height, frameSize.height);
frameSize.width = Math.min(screenSize.width, frameSize.width);
setLocation((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2);
addMenu();
addButton();
copyPomFromJar();
try
{
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
SwingUtilities.updateComponentTreeUI(this);
}
catch (Exception e)
{
}
setResizable(false);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
fileText.setFocusable(false);
}
/**
* jar 拷贝样例pom
*
* @throws IOException
*
*/
private void copyPomFromJar()
{
URL url = QuickSourceUI.class.getProtectionDomain().getCodeSource().getLocation();
if (url.getPath().endsWith(".jar"))
{
try (InputStream is = QuickSourceUI.class.getResourceAsStream("/pom.xml"))
{
File file = new File(DateFormatUtils.format(System.currentTimeMillis(), "yyyyMMdd-HHmmss") + "-pom.xml");
FileUtils.copyInputStreamToFile(is, file);
file.deleteOnExit();
}
catch (IOException e)
{
log.error(e.getMessage(), e);
}
}
}
// Menu set
private void addMenu()
{
JMenuBar mb = new JMenuBar();
// 一级菜单
JMenu conf = new JMenu(" 系 统 ");
// 子菜单
JMenuItem exit = new JMenuItem("退出");
exit.addActionListener(event -> System.exit(0));
conf.add(exit);
mb.add(conf);
JMenu help = new JMenu(" 帮 助 ");
JMenuItem about = new JMenuItem("关于工具");
about.addActionListener((ActionEvent event) -> JOptionPane.showMessageDialog(null, "POM源码工具 V1.0,00fly 于2024年11月。\n", "关于本工具", JOptionPane.INFORMATION_MESSAGE));
help.add(about);
mb.add(help);
setJMenuBar(mb);
}
// JButton set
private void addButton()
{
panel.setLayout(null);
getContentPane().add(panel);
JLabel textLabel = new JLabel("pom文件内容");
textLabel.setBounds(20, 10, 120, 18);
panel.add(textLabel);
JLabel fileLabel = new JLabel("POM文件");
fileLabel.setBounds(30, 700, 240, 18);
panel.add(fileLabel);
textArea.setEditable(false);
JScrollPane scroll = new JScrollPane(textArea);
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scroll.setBounds(20, 30, 950, 650);
panel.add(scroll);
fileText.setBounds(100, 700, 560, 24);
fileText.setText(new File(" ").getAbsolutePath().trim());
panel.add(fileText);
fileDirBrowse.setBounds(680, 700, 80, 25);
fileDirBrowse.addActionListener(event -> {
String path = fileText.getText();
File xmlfile = new File(path);
if (new File(fileText.getText()).exists())
{
xmlfile = new File(path).getParentFile();
}
JFileChooser fc = new JFileChooser(xmlfile);
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
fc.setDialogTitle("pom.xml文件选择");
if (fc.showOpenDialog(null) == JFileChooser.CANCEL_OPTION)
{
return;
}
fc.setFileFilter(new FileFilter()
{
@Override
public boolean accept(File file)
{
return file.getName().toLowerCase().endsWith(".xml");
}
@Override
public String getDescription()
{
return "";
}
});
File f = fc.getSelectedFile();
if (f != null && f.isFile())
{
fileText.setText(f.getAbsolutePath());
try
{
textArea.setText(FileUtils.readFileToString(f, StandardCharsets.UTF_8));
runButton.setEnabled(true);
}
catch (IOException e)
{
}
}
});
panel.add(fileDirBrowse);
runButton.addMouseListener(new MouseAdapter()
{
@Override
public void mouseClicked(MouseEvent e)
{
if (!runButton.isEnabled())
{
return;
}
new Thread(() -> {
try
{
runButton.setText(" 处 理 中 ......");
runButton.setEnabled(false);
// 业务处理
String pomPath = fileText.getText();
if (!new File(pomPath).exists())
{
throw new Exception(pomPath + "文件不存在");
}
// 移动pom文件
File dest = new File("upload/pom.xml");
FileUtils.copyFile(new File(pomPath), dest);
// 运行package命令,通过pom文件中的插件maven-dependency-plugin收集jar到lib
pomPath = dest.getCanonicalPath();
log.info("pom.xml 文件路径:{}", pomPath);
Executor.execute("mvn clean package -f " + pomPath).forEach(log::info);
// 收集sources
MvnUtils.copyResource(pomPath);
setExtendedState(Frame.ICONIFIED);
runButton.setText(" 获 取 源 码 包 ");
runButton.setEnabled(true);
}
catch (Exception e1)
{
JOptionPane.showMessageDialog(null, "失败原因: " + e1.getMessage(), "业务处理失败", JOptionPane.ERROR_MESSAGE);
}
}).start();
}
});
runButton.setBounds(780, 695, 160, 30);
runButton.setEnabled(false);
panel.add(runButton);
}
// Run
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> new QuickSourceUI());
}
}
四、源码放送
可执行Jar下载: quick-source-get-0.0.1.jar (6.3M)
swing版:https://gitee.com/00fly/effict-side/tree/master/quick-source-get
springboot版:https://gitee.com/00fly/effict-side/tree/master/quick-source-get-boot
有任何问题和建议,都可以向我提问讨论,大家一起进步,谢谢!
-over-