per plugin

This commit is contained in:
bwcx_jzy 2021-12-22 13:38:54 +08:00
parent 7c203976cd
commit 3403c08419
No known key found for this signature in database
GPG Key ID: 5E48E9372088B9E5
13 changed files with 426 additions and 71 deletions

View File

@ -97,6 +97,7 @@
<fork>true</fork>
<mainClass>${start-class}</mainClass>
<jvmArguments>-Dfile.encoding=UTF-8</jvmArguments>
<excludeGroupIds>io.jpom.plugin</excludeGroupIds>
</configuration>
<executions>
<execution>

View File

@ -0,0 +1,45 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Code Technology Studio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.jpom.plugin;
/**
* @author bwcx_jzy
* @since 2021/12/22
*/
public enum DefaultPlugin {
/**
* web hook
*/
WebHook("webhook"),
;
private final String name;
DefaultPlugin(String name) {
this.name = name;
}
public String getName() {
return name;
}
}

View File

@ -0,0 +1,42 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Code Technology Studio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.jpom.plugin;
/**
* 插件模块接口
*
* @author bwcx_jzy
* @since 2021/12/22
*/
public interface IDefaultPlugin extends IPlugin {
/**
* 默认插件排序到最后
*
* @return max
*/
@Override
default int order() {
return Integer.MAX_VALUE;
}
}

View File

@ -0,0 +1,59 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Code Technology Studio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.jpom.plugin;
import java.util.Map;
/**
* 插件模块接口
*
* @author bwcx_jzy
* @since 2021/12/22
*/
public interface IPlugin {
/**
* 执行插件方法
*
* @param main 拦截到到对象
* @param parameter 执行方法传人的参数
* @return 返回值
*/
Object execute(Object main, Map<String, Object> parameter);
/**
* 插件的名字
*
* @return 名称
*/
String name();
/**
* 排序值
*
* @return 值越小排到前面 正序
*/
default int order() {
return Integer.MIN_VALUE;
}
}

View File

@ -22,19 +22,26 @@
*/
package io.jpom.plugin;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.JarClassLoader;
import cn.hutool.core.util.ClassLoaderUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.jiangzeyin.common.DefaultSystemLog;
import io.jpom.common.JpomManifest;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.Assert;
import java.io.File;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.ToIntFunction;
import java.util.stream.Collectors;
/**
* 插件工厂
@ -44,58 +51,135 @@ import java.util.List;
*/
public class PluginFactory implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final List<FeatureCallback> FEATURE_CALLBACKS = new ArrayList<>();
private static final List<FeatureCallback> FEATURE_CALLBACKS = new ArrayList<>();
private static final Map<String, List<PluginItem>> PLUGIN_MAP = new ConcurrentHashMap<>();
/**
* 添加回调事件
*
* @param featureCallback 回调
*/
public static void addFeatureCallback(FeatureCallback featureCallback) {
FEATURE_CALLBACKS.add(featureCallback);
}
/**
* 添加回调事件
*
* @param featureCallback 回调
*/
public static void addFeatureCallback(FeatureCallback featureCallback) {
FEATURE_CALLBACKS.add(featureCallback);
}
public static List<FeatureCallback> getFeatureCallbacks() {
return FEATURE_CALLBACKS;
}
public static List<FeatureCallback> getFeatureCallbacks() {
return FEATURE_CALLBACKS;
}
/**
* 正式环境添加依赖
*/
private static void init() {
if (JpomManifest.getInstance().isDebug()) {
return;
}
File runPath = JpomManifest.getRunPath().getParentFile();
File plugin = FileUtil.file(runPath, "plugin");
if (!plugin.exists() || plugin.isFile()) {
return;
}
File[] files = plugin.listFiles(File::isDirectory);
if (files == null) {
return;
}
for (File file : files) {
File lib = FileUtil.file(file, "lib");
if (!lib.exists() || lib.isFile()) {
continue;
}
File[] listFiles = lib.listFiles((dir, name) -> StrUtil.endWith(name, FileUtil.JAR_FILE_EXT, true));
if (listFiles == null || listFiles.length <= 0) {
continue;
}
addPlugin(file.getName(), lib);
}
}
/**
* 获取插件端
*
* @param plugin 插件
* @return 插件对象
*/
public static IPlugin getPlugin(DefaultPlugin plugin) {
return getPlugin(plugin.getName());
}
private static void addPlugin(String pluginName, File file) {
DefaultSystemLog.getLog().info("加载:{}插件", pluginName);
ClassLoader contextClassLoader = ClassLoaderUtil.getClassLoader();
JarClassLoader.loadJar((URLClassLoader) contextClassLoader, file);
}
/**
* 获取插件端
*
* @param name 插件名
* @return 插件对象
*/
public static IPlugin getPlugin(String name) {
List<PluginItem> pluginItems = PLUGIN_MAP.get(name);
PluginItem first = CollUtil.getFirst(pluginItems);
Assert.notNull(first, "对应找到对应到插件:" + name);
return first.plugin;
}
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
init();
}
/**
* 正式环境添加依赖
*/
private static void init() {
if (JpomManifest.getInstance().isDebug()) {
return;
}
File runPath = JpomManifest.getRunPath().getParentFile();
File plugin = FileUtil.file(runPath, "plugin");
if (!plugin.exists() || plugin.isFile()) {
return;
}
File[] dirFiles = plugin.listFiles(File::isDirectory);
if (dirFiles != null) {
for (File file : dirFiles) {
File lib = FileUtil.file(file, "lib");
if (!lib.exists() || lib.isFile()) {
continue;
}
File[] listFiles = lib.listFiles((dir, name) -> StrUtil.endWith(name, FileUtil.JAR_FILE_EXT, true));
if (listFiles == null || listFiles.length <= 0) {
continue;
}
addPlugin(file.getName(), lib);
}
}
File[] files = plugin.listFiles(pathname -> FileUtil.isFile(pathname) && FileUtil.JAR_FILE_EXT.equalsIgnoreCase(FileUtil.extName(pathname)));
if (files != null) {
for (File file : files) {
addPlugin(file.getName(), file);
}
}
}
private static void addPlugin(String pluginName, File file) {
DefaultSystemLog.getLog().info("加载:{}插件", pluginName);
ClassLoader contextClassLoader = ClassLoaderUtil.getClassLoader();
JarClassLoader.loadJar((URLClassLoader) contextClassLoader, file);
}
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
init();
// 扫描插件 实现
Set<Class<?>> classes = ClassUtil.scanPackage("io.jpom", IPlugin.class::isAssignableFrom);
List<PluginItem> pluginItems = classes.stream().filter(ClassUtil::isNormalClass).map(aClass -> {
PluginItem pluginItem = new PluginItem();
IPlugin plugin = (IPlugin) ReflectUtil.newInstance(aClass);
pluginItem.plugin = plugin;
pluginItem.name = plugin.name();
pluginItem.className = (Class<? extends IPlugin>) aClass;
return pluginItem;
}).collect(Collectors.toList());
//
Map<String, List<PluginItem>> pluginMap = CollStreamUtil.groupByKey(pluginItems, PluginItem::getName);
pluginMap.forEach((key, value) -> {
// 排序
value.sort((o1, o2) -> Comparator.comparingInt((ToIntFunction<PluginItem>) value1 -> value1.plugin.order()).compare(o1, o2));
PLUGIN_MAP.put(key, value);
});
}
private static class PluginItem {
/**
* 插件名
*/
private String name;
/**
* 插件类名
*/
private Class<? extends IPlugin> className;
/**
* 插件对象
*/
private IPlugin plugin;
public String getName() {
return name;
}
public Class<? extends IPlugin> getClassName() {
return className;
}
public IPlugin getPlugin() {
return plugin;
}
}
}

View File

@ -17,13 +17,18 @@
</properties>
<dependencies>
<dependency>
<groupId>io.jpom</groupId>
<artifactId>common</artifactId>
<version>${pom.version}</version>
</dependency>
<dependency>
<groupId>io.jpom.plugin</groupId>
<artifactId>webhook</artifactId>
<version>${pom.version}</version>
</dependency>
<!--websocket作为客户端-->
<dependency>
<groupId>org.java-websocket</groupId>
@ -133,6 +138,7 @@
<fork>true</fork>
<mainClass>${start-class}</mainClass>
<jvmArguments>-Dfile.encoding=UTF-8</jvmArguments>
<excludeGroupIds>io.jpom.plugin</excludeGroupIds>
</configuration>
<executions>
<execution>

View File

@ -53,6 +53,12 @@
<include>io.jpom:server</include>
</includes>
</dependencySet>
<dependencySet>
<outputDirectory>lib/plugins</outputDirectory>
<includes>
<include>io.jpom.plugin</include>
</includes>
</dependencySet>
</dependencySets>
</assembly>

View File

@ -29,13 +29,10 @@ import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.LineHandler;
import cn.hutool.core.io.file.FileCopier;
import cn.hutool.core.lang.Tuple;
import cn.hutool.core.text.CharPool;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.jiangzeyin.common.DefaultSystemLog;
import cn.jiangzeyin.common.spring.SpringUtil;
import io.jpom.common.BaseServerController;
@ -45,6 +42,9 @@ import io.jpom.model.data.UserModel;
import io.jpom.model.enums.BuildReleaseMethod;
import io.jpom.model.enums.BuildStatus;
import io.jpom.model.log.BuildHistoryLog;
import io.jpom.plugin.DefaultPlugin;
import io.jpom.plugin.IPlugin;
import io.jpom.plugin.PluginFactory;
import io.jpom.service.dblog.BuildInfoService;
import io.jpom.service.dblog.DbBuildHistoryLogService;
import io.jpom.system.ExtConfigBean;
@ -63,10 +63,7 @@ import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
@ -484,20 +481,21 @@ public class BuildInfoManage extends BaseBuild implements Runnable {
*/
private void asyncWebHooks(String type, Object... other) {
String webhook = this.buildInfoModel.getWebhook();
if (StrUtil.isEmpty(webhook)) {
return;
}
IPlugin plugin = PluginFactory.getPlugin(DefaultPlugin.WebHook);
Map<String, Object> map = new HashMap<>(10);
long triggerTime = SystemClock.now();
map.put("buildId", this.buildInfoModel.getId());
map.put("buildName", this.buildInfoModel.getName());
map.put("type", type);
map.put("triggerBuildType", this.triggerBuildType);
map.put("triggerTime", triggerTime);
//
for (int i = 0; i < other.length; i += 2) {
map.put(other[i].toString(), other[i + 1]);
}
ThreadUtil.execute(() -> {
try {
HttpRequest httpRequest = HttpUtil.createGet(webhook);
httpRequest.form("buildId", this.buildInfoModel.getId());
httpRequest.form("buildName", this.buildInfoModel.getName());
httpRequest.form("type", type, other);
httpRequest.form("triggerBuildType", this.triggerBuildType);
httpRequest.form("triggerTime", triggerTime);
String body = httpRequest.execute().body();
DefaultSystemLog.getLog().info(this.buildInfoModel.getName() + CharPool.COLON + body);
plugin.execute(webhook, map);
} catch (Exception e) {
DefaultSystemLog.getLog().error("WebHooks 调用错误", e);
}

View File

@ -1,3 +1,25 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Code Technology Studio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.jpom.model.data;
import io.jpom.model.BaseJsonModel;

View File

@ -20,7 +20,6 @@
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import cn.hutool.core.io.FileUtil;
import io.jpom.util.CommandUtil;
import org.junit.Test;

View File

@ -0,0 +1,27 @@
<?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>jpom-parent</artifactId>
<groupId>io.jpom</groupId>
<version>2.8.1</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>io.jpom.plugin</groupId>
<artifactId>webhook</artifactId>
<properties>
</properties>
<dependencies>
<dependency>
<groupId>io.jpom</groupId>
<artifactId>common</artifactId>
<version>${pom.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,65 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Code Technology Studio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.jpom.webhook;
import cn.hutool.core.text.CharPool;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.jiangzeyin.common.DefaultSystemLog;
import io.jpom.plugin.DefaultPlugin;
import io.jpom.plugin.IDefaultPlugin;
import java.util.Map;
/**
* 默认到 webhook 实现
*
* @author bwcx_jzy
* @since 2021/12/22
*/
public class DefaultSimplePluginImpl implements IDefaultPlugin {
@Override
public Object execute(Object main, Map<String, Object> parameter) {
String webhook = StrUtil.toString(main);
if (StrUtil.isEmpty(webhook)) {
return null;
}
try {
HttpRequest httpRequest = HttpUtil.createGet(webhook);
httpRequest.form(parameter);
String body = httpRequest.execute().body();
DefaultSystemLog.getLog().info(webhook + CharPool.COLON + body);
return body;
} catch (Exception e) {
DefaultSystemLog.getLog().error("WebHooks 调用错误", e);
}
return null;
}
@Override
public String name() {
return DefaultPlugin.WebHook.getName();
}
}

View File

@ -8,6 +8,7 @@
<module>modules/agent</module>
<module>modules/server</module>
<module>modules/common</module>
<module>modules/sub-plugin/webhook</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>