This is an automated email from the ASF dual-hosted git repository.
liutianyou pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git
The following commit(s) were added to refs/heads/master by this push:
new 0e49e100d [feature] Implement custom parameters for plugins (#2616)
0e49e100d is described below
commit 0e49e100d8fdcf8ae6e9248c15519fb6f69f16f4
Author: linDong <[email protected]>
AuthorDate: Thu Aug 29 20:33:17 2024 +0800
[feature] Implement custom parameters for plugins (#2616)
Signed-off-by: liutianyou <[email protected]>
Co-authored-by: shown <[email protected]>
Co-authored-by: Kerwin Bryant <[email protected]>
Co-authored-by: Logic <[email protected]>
Co-authored-by: liutianyou <[email protected]>
---
.../manager/component/alerter/DispatcherAlarm.java | 2 +-
.../manager/controller/PluginController.java | 17 +++
.../hertzbeat/manager/dao/PluginParamDao.java | 48 ++++++++
.../hertzbeat/manager/pojo/dto/PluginParam.java | 110 +++++++++++++++++
.../manager/pojo/dto/PluginParametersVO.java | 24 +++-
.../hertzbeat/manager/service/PluginService.java | 19 ++-
.../manager/service/impl/PluginServiceImpl.java | 131 +++++++++++++++++++--
.../manager/service/PluginServiceTest.java | 7 +-
.../java/org/apache/hertzbeat/plugin/Plugin.java | 8 ++
plugin/src/main/resources/define/define.yml | 14 +++
.../routes/setting/plugins/plugin.component.html | 24 ++++
.../app/routes/setting/plugins/plugin.component.ts | 62 ++++++++++
web-app/src/app/service/plugin.service.ts | 27 ++---
.../form-field/form-field.component.html | 2 +-
web-app/src/assets/i18n/en-US.json | 1 +
web-app/src/assets/i18n/zh-CN.json | 1 +
web-app/src/assets/i18n/zh-TW.json | 1 +
17 files changed, 466 insertions(+), 32 deletions(-)
diff --git
a/manager/src/main/java/org/apache/hertzbeat/manager/component/alerter/DispatcherAlarm.java
b/manager/src/main/java/org/apache/hertzbeat/manager/component/alerter/DispatcherAlarm.java
index 0e688e1c7..c543c08a1 100644
---
a/manager/src/main/java/org/apache/hertzbeat/manager/component/alerter/DispatcherAlarm.java
+++
b/manager/src/main/java/org/apache/hertzbeat/manager/component/alerter/DispatcherAlarm.java
@@ -131,7 +131,7 @@ public class DispatcherAlarm implements InitializingBean {
// Notice distribution
sendNotify(alert);
// Execute the plugin if enable
- pluginService.pluginExecute(Plugin.class, plugin ->
plugin.alert(alert));
+ pluginService.pluginExecute(Plugin.class, plugin ->
plugin.alert(alert), (plugin, configMapList) -> plugin.alert(alert,
configMapList));
}
} catch (IgnoreException ignored) {
} catch (InterruptedException e) {
diff --git
a/manager/src/main/java/org/apache/hertzbeat/manager/controller/PluginController.java
b/manager/src/main/java/org/apache/hertzbeat/manager/controller/PluginController.java
index 1fb73ada7..18c24544f 100644
---
a/manager/src/main/java/org/apache/hertzbeat/manager/controller/PluginController.java
+++
b/manager/src/main/java/org/apache/hertzbeat/manager/controller/PluginController.java
@@ -27,6 +27,8 @@ import lombok.RequiredArgsConstructor;
import org.apache.hertzbeat.common.entity.dto.Message;
import org.apache.hertzbeat.common.entity.dto.PluginUpload;
import org.apache.hertzbeat.common.entity.manager.PluginMetadata;
+import org.apache.hertzbeat.manager.pojo.dto.PluginParam;
+import org.apache.hertzbeat.manager.pojo.dto.PluginParametersVO;
import org.apache.hertzbeat.manager.service.PluginService;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
@@ -85,4 +87,19 @@ public class PluginController {
pluginService.updateStatus(plugin);
return ResponseEntity.ok(Message.success("Update success"));
}
+
+ @GetMapping("/params/define")
+ @Operation(summary = "get param define", description = "get param define
by jar path")
+ public ResponseEntity<Message<PluginParametersVO>>
getParamDefine(@RequestParam Long pluginMetadataId) {
+ PluginParametersVO plugins =
pluginService.getParamDefine(pluginMetadataId);
+ return ResponseEntity.ok(Message.success(plugins));
+ }
+
+ @PostMapping("/params")
+ @Operation(summary = "get param define", description = "get param define
by jar path")
+ public ResponseEntity<Message<Boolean>> saveParams(@RequestBody
List<PluginParam> pluginParams) {
+ pluginService.savePluginParam(pluginParams);
+ return ResponseEntity.ok(Message.success(true));
+ }
+
}
diff --git
a/manager/src/main/java/org/apache/hertzbeat/manager/dao/PluginParamDao.java
b/manager/src/main/java/org/apache/hertzbeat/manager/dao/PluginParamDao.java
new file mode 100644
index 000000000..4eeec26b1
--- /dev/null
+++ b/manager/src/main/java/org/apache/hertzbeat/manager/dao/PluginParamDao.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hertzbeat.manager.dao;
+
+import java.util.List;
+import java.util.Set;
+import org.apache.hertzbeat.manager.pojo.dto.PluginParam;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+/**
+ * PluginParamDao database operations
+ */
+public interface PluginParamDao extends JpaRepository<PluginParam, Long> {
+
+ /**
+ * Query the list of parameters associated with the monitoring ID'
+ * @param pluginMetadataId Monitor ID
+ * @return list of parameter values
+ */
+ List<PluginParam> findParamsByPluginMetadataId(Long pluginMetadataId);
+
+ /**
+ * Remove the parameter list associated with the pluginMetadata ID based
on it
+ * @param pluginMetadataId Monitor Id
+ */
+ void deletePluginParamsByPluginMetadataId(long pluginMetadataId);
+
+ /**
+ * Remove the parameter list associated with the pluginMetadata ID list
based on it
+ * @param pluginMetadataIds Monitoring ID List
+ */
+ void deletePluginParamsByPluginMetadataIdIn(Set<Long> pluginMetadataIds);
+}
diff --git
a/manager/src/main/java/org/apache/hertzbeat/manager/pojo/dto/PluginParam.java
b/manager/src/main/java/org/apache/hertzbeat/manager/pojo/dto/PluginParam.java
new file mode 100644
index 000000000..b92948f2b
--- /dev/null
+++
b/manager/src/main/java/org/apache/hertzbeat/manager/pojo/dto/PluginParam.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hertzbeat.manager.pojo.dto;
+
+import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
+import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EntityListeners;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Index;
+import jakarta.persistence.Table;
+import jakarta.persistence.UniqueConstraint;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import java.time.LocalDateTime;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+/**
+ * PluginParam
+ */
+@Entity
+@Table(name = "hzb_plugin_param", indexes = { @Index(columnList =
"pluginMetadataId") },
+ uniqueConstraints = @UniqueConstraint(columnNames =
{"pluginMetadataId", "field"}))
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Schema(description = "Parameter Entity")
+@EntityListeners(AuditingEntityListener.class)
+public class PluginParam {
+
+ /**
+ * Parameter primary key index ID
+ */
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Schema(title = "Parameter primary key index ID", example = "87584674384",
accessMode = READ_ONLY)
+ private Long id;
+ /**
+ * Monitor ID
+ */
+ @Schema(title = "Plugin task ID", example = "875846754543", accessMode =
READ_WRITE)
+ @NotNull
+ private Long pluginMetadataId;
+
+ /**
+ * Parameter Field Identifier
+ */
+ @Schema(title = "Parameter identifier field", example = "port", accessMode
= READ_WRITE)
+ @Size(max = 100)
+ @NotNull
+ private String field;
+
+ /**
+ * Param Value
+ */
+ @Schema(title = "parameter values", example = "8080", accessMode =
READ_WRITE)
+ @Size(max = 8126)
+ @Column(length = 8126)
+ private String paramValue;
+
+ /**
+ * Parameter type 0: number 1: string 2: encrypted string 3: json string
mapped by map
+ */
+ @Schema(title = "Parameter types 0: number 1: string 2: encrypted string
3:map mapped json string 4:arrays string",
+ accessMode = READ_WRITE)
+ @Min(0)
+ private byte type;
+
+ /**
+ * Record create time
+ */
+ @Schema(title = "Record create time", example = "1612198922000",
accessMode = READ_ONLY)
+ @CreatedDate
+ private LocalDateTime gmtCreate;
+
+ /**
+ * Record the latest modification time
+ */
+ @Schema(title = "Record modify time", example = "1612198444000",
accessMode = READ_ONLY)
+ @LastModifiedDate
+ private LocalDateTime gmtUpdate;
+
+}
diff --git a/plugin/src/main/java/org/apache/hertzbeat/plugin/Plugin.java
b/manager/src/main/java/org/apache/hertzbeat/manager/pojo/dto/PluginParametersVO.java
similarity index 62%
copy from plugin/src/main/java/org/apache/hertzbeat/plugin/Plugin.java
copy to
manager/src/main/java/org/apache/hertzbeat/manager/pojo/dto/PluginParametersVO.java
index d72632925..ba3f2f9ff 100644
--- a/plugin/src/main/java/org/apache/hertzbeat/plugin/Plugin.java
+++
b/manager/src/main/java/org/apache/hertzbeat/manager/pojo/dto/PluginParametersVO.java
@@ -15,17 +15,29 @@
* limitations under the License.
*/
-package org.apache.hertzbeat.plugin;
+package org.apache.hertzbeat.manager.pojo.dto;
-import org.apache.hertzbeat.common.entity.alerter.Alert;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.hertzbeat.common.entity.manager.ParamDefine;
+import java.util.List;
/**
- * Plugin
+ * Popup rendering and parameter values
*/
-public interface Plugin {
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class PluginParametersVO {
/**
- * execute when alert
+ * Stencil rendering
*/
- void alert(Alert alert);
+ private List<ParamDefine> paramDefines;
+
+ /**
+ * specific parameter
+ */
+ private List<PluginParam> pluginParams;
}
diff --git
a/manager/src/main/java/org/apache/hertzbeat/manager/service/PluginService.java
b/manager/src/main/java/org/apache/hertzbeat/manager/service/PluginService.java
index 23d01a30b..a7f0c706e 100644
---
a/manager/src/main/java/org/apache/hertzbeat/manager/service/PluginService.java
+++
b/manager/src/main/java/org/apache/hertzbeat/manager/service/PluginService.java
@@ -17,10 +17,15 @@
package org.apache.hertzbeat.manager.service;
+import java.util.List;
import java.util.Set;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.apache.hertzbeat.common.entity.dto.PluginUpload;
+import org.apache.hertzbeat.common.entity.job.Configmap;
import org.apache.hertzbeat.common.entity.manager.PluginMetadata;
+import org.apache.hertzbeat.manager.pojo.dto.PluginParam;
+import org.apache.hertzbeat.manager.pojo.dto.PluginParametersVO;
import org.springframework.data.domain.Page;
/**
@@ -58,7 +63,7 @@ public interface PluginService {
* @param execute run plugin logic
* @param <T> plugin type
*/
- <T> void pluginExecute(Class<T> clazz, Consumer<T> execute);
+ <T> void pluginExecute(Class<T> clazz, Consumer<T> execute, BiConsumer<T,
List<Configmap>> biConsumer);
/**
* delete plugin
@@ -69,4 +74,16 @@ public interface PluginService {
void updateStatus(PluginMetadata plugin);
+ /**
+ * get param define
+ * @param pluginMetadataId plugin id
+ */
+ PluginParametersVO getParamDefine(Long pluginMetadataId);
+
+ /**
+ * save plugin param
+ * @param params params
+ */
+ void savePluginParam(List<PluginParam> params);
+
}
diff --git
a/manager/src/main/java/org/apache/hertzbeat/manager/service/impl/PluginServiceImpl.java
b/manager/src/main/java/org/apache/hertzbeat/manager/service/impl/PluginServiceImpl.java
index 162d4bc39..4ccae0fd0 100644
---
a/manager/src/main/java/org/apache/hertzbeat/manager/service/impl/PluginServiceImpl.java
+++
b/manager/src/main/java/org/apache/hertzbeat/manager/service/impl/PluginServiceImpl.java
@@ -39,9 +39,11 @@ import java.util.ServiceLoader;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
+import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
@@ -49,17 +51,26 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.hertzbeat.common.constants.PluginType;
import org.apache.hertzbeat.common.entity.dto.PluginUpload;
+import org.apache.hertzbeat.common.entity.job.Configmap;
+import org.apache.hertzbeat.common.entity.manager.ParamDefine;
import org.apache.hertzbeat.common.entity.manager.PluginItem;
import org.apache.hertzbeat.common.entity.manager.PluginMetadata;
import org.apache.hertzbeat.common.support.exception.CommonException;
import org.apache.hertzbeat.manager.dao.PluginItemDao;
import org.apache.hertzbeat.manager.dao.PluginMetadataDao;
+import org.apache.hertzbeat.manager.dao.PluginParamDao;
+import org.apache.hertzbeat.manager.pojo.dto.PluginParam;
+import org.apache.hertzbeat.manager.pojo.dto.PluginParametersVO;
import org.apache.hertzbeat.manager.service.PluginService;
import org.apache.hertzbeat.plugin.Plugin;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.error.YAMLException;
/**
* plugin service
@@ -73,6 +84,8 @@ public class PluginServiceImpl implements PluginService {
private final PluginItemDao itemDao;
+ private final PluginParamDao pluginParamDao;
+
public static Map<Class<?>, PluginType> PLUGIN_TYPE_MAPPING = new
HashMap<>();
/**
@@ -80,10 +93,25 @@ public class PluginServiceImpl implements PluginService {
*/
private static final Map<String, Boolean> PLUGIN_ENABLE_STATUS = new
ConcurrentHashMap<>();
+ /**
+ * plugin param define
+ */
+ private static final Map<Long, List<ParamDefine>> PARAMS_DEFINE_MAP = new
ConcurrentHashMap<>();
+
+ /**
+ * plugin params
+ */
+ private static final Map<Long, List<Configmap>> PARAMS_MAP = new
ConcurrentHashMap<>();
+
+ /**
+ * pluginItem Mapping pluginId
+ */
+ private static final Map<String, Long> ITEM_TO_PLUGINMETADATAID_MAP = new
ConcurrentHashMap<>();
private final List<URLClassLoader> pluginClassLoaders = new ArrayList<>();
@Override
+ @Transactional
public void deletePlugins(Set<Long> ids) {
List<PluginMetadata> plugins = metadataDao.findAllById(ids);
// disable the plugins that need to be removed
@@ -106,11 +134,13 @@ public class PluginServiceImpl implements PluginService {
}
// delete metadata
metadataDao.deleteById(plugin.getId());
+ syncPluginParamMap(plugin.getId(), null, true);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
+ pluginParamDao.deletePluginParamsByPluginMetadataIdIn(ids);
syncPluginStatus();
// reload classloader
loadJarToClassLoader();
@@ -139,6 +169,40 @@ public class PluginServiceImpl implements PluginService {
}
}
+ @Override
+ public PluginParametersVO getParamDefine(Long pluginMetadataId) {
+
+ PluginParametersVO pluginParametersVO = new PluginParametersVO();
+ if (PARAMS_DEFINE_MAP.containsKey(pluginMetadataId)) {
+ List<ParamDefine> paramDefines =
PARAMS_DEFINE_MAP.get(pluginMetadataId);
+ List<PluginParam> paramsByPluginMetadataId =
pluginParamDao.findParamsByPluginMetadataId(pluginMetadataId);
+ pluginParametersVO.setParamDefines(paramDefines);
+ pluginParametersVO.setPluginParams(paramsByPluginMetadataId);
+ return pluginParametersVO;
+ }
+ return pluginParametersVO;
+ }
+
+ @Override
+ @Transactional
+ public void savePluginParam(List<PluginParam> params) {
+ if (CollectionUtils.isEmpty(params)) {
+ return;
+ }
+
pluginParamDao.deletePluginParamsByPluginMetadataId(params.get(0).getPluginMetadataId());
+ pluginParamDao.saveAll(params);
+ syncPluginParamMap(params.get(0).getPluginMetadataId(), params, false);
+ }
+
+ private void syncPluginParamMap(Long pluginMetadataId, List<PluginParam>
params, boolean isDelete) {
+ if (isDelete) {
+ PARAMS_MAP.remove(pluginMetadataId);
+ return;
+ }
+ List<Configmap> configmapList = params.stream().map(item -> new
Configmap(item.getField(), item.getParamValue(), item.getType())).toList();
+ PARAMS_MAP.put(pluginMetadataId, configmapList);
+ }
+
static {
PLUGIN_TYPE_MAPPING.put(Plugin.class, PluginType.POST_ALERT);
}
@@ -154,8 +218,9 @@ public class PluginServiceImpl implements PluginService {
try {
URL jarUrl = new URL("file:" + jarFile.getAbsolutePath());
try (URLClassLoader classLoader = new URLClassLoader(new
URL[]{jarUrl}, this.getClass().getClassLoader());
- JarFile jar = new JarFile(jarFile)) {
+ JarFile jar = new JarFile(jarFile)) {
Enumeration<JarEntry> entries = jar.entries();
+ Yaml yaml = new Yaml();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.getName().endsWith(".class")) {
@@ -173,6 +238,11 @@ public class PluginServiceImpl implements PluginService {
System.err.println("Failed to load class: " +
className);
}
}
+ if ((entry.getName().contains("define")) &&
(entry.getName().endsWith(".yml") || entry.getName().endsWith(".yaml"))) {
+ try (InputStream ymlInputStream =
jar.getInputStream(entry)) {
+ yaml.loadAs(ymlInputStream, List.class);
+ }
+ }
}
if (pluginItems.isEmpty()) {
throw new CommonException("Illegal plug-ins, please refer
to https://hertzbeat.apache.org/docs/help/plugin/");
@@ -184,6 +254,8 @@ public class PluginServiceImpl implements PluginService {
} catch (MalformedURLException e) {
log.error("Invalid JAR file URL: {}", jarFile.getAbsoluteFile(),
e);
throw new CommonException("Invalid JAR file URL: " +
jarFile.getAbsolutePath());
+ } catch (YAMLException e) {
+ throw new CommonException("YAML the file format is incorrect");
}
return pluginItems;
}
@@ -196,6 +268,7 @@ public class PluginServiceImpl implements PluginService {
@Override
@SneakyThrows
+ @Transactional
public void savePlugin(PluginUpload pluginUpload) {
String jarPath = new
File(this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath()).getAbsolutePath();
Path extLibPath = Paths.get(new File(jarPath).getParent(),
"plugin-lib");
@@ -268,13 +341,33 @@ public class PluginServiceImpl implements PluginService {
private void syncPluginStatus() {
List<PluginMetadata> plugins = metadataDao.findAll();
Map<String, Boolean> statusMap = new HashMap<>();
+ Map<String, Long> itemToPluginMetadataIdMap = new HashMap<>();
for (PluginMetadata plugin : plugins) {
for (PluginItem item : plugin.getItems()) {
statusMap.put(item.getClassIdentifier(),
plugin.getEnableStatus());
+ itemToPluginMetadataIdMap.put(item.getClassIdentifier(),
plugin.getId());
}
}
PLUGIN_ENABLE_STATUS.clear();
PLUGIN_ENABLE_STATUS.putAll(statusMap);
+ ITEM_TO_PLUGINMETADATAID_MAP.clear();
+ ITEM_TO_PLUGINMETADATAID_MAP.putAll(itemToPluginMetadataIdMap);
+ }
+
+ @PostConstruct
+ private void initParams(){
+ try {
+ List<PluginParam> params = pluginParamDao.findAll();
+ Map<Long, List<PluginParam>> content = params.stream()
+
.collect(Collectors.groupingBy(PluginParam::getPluginMetadataId));
+
+ for (Map.Entry<Long, List<PluginParam>> entry :
content.entrySet()) {
+ syncPluginParamMap(entry.getKey(), entry.getValue(), false);
+ }
+ } catch (Exception e) {
+ log.error("Failed to init params:{}", e.getMessage());
+ throw new CommonException("Failed to init params:" +
e.getMessage());
+ }
}
/**
@@ -288,14 +381,19 @@ public class PluginServiceImpl implements PluginService {
pluginClassLoader.close();
}
}
- pluginClassLoaders.clear();
- System.gc();
+
+ if (!pluginClassLoaders.isEmpty()) {
+ pluginClassLoaders.clear();
+ System.gc();
+ }
+ PARAMS_DEFINE_MAP.clear();
List<PluginMetadata> plugins =
metadataDao.findPluginMetadataByEnableStatusTrue();
for (PluginMetadata metadata : plugins) {
- List<URL> urls = loadLibInPlugin(metadata.getJarFilePath());
+ List<URL> urls = loadLibInPlugin(metadata.getJarFilePath(),
metadata.getId());
urls.add(new File(metadata.getJarFilePath()).toURI().toURL());
pluginClassLoaders.add(new URLClassLoader(urls.toArray(new
URL[0]), Plugin.class.getClassLoader()));
}
+
} catch (MalformedURLException e) {
log.error("Failed to load plugin:{}", e.getMessage());
throw new CommonException("Failed to load plugin:" +
e.getMessage());
@@ -308,19 +406,24 @@ public class PluginServiceImpl implements PluginService {
* loading other JAR files that are dependencies for the plugin
*
* @param pluginJarPath jar file path
+ * @param pluginMetadataId plugin id
* @return urls
*/
@SneakyThrows
- private List<URL> loadLibInPlugin(String pluginJarPath) {
+ private List<URL> loadLibInPlugin(String pluginJarPath, Long
pluginMetadataId) {
File libDir = new File(getOtherLibDir(pluginJarPath));
FileUtils.forceMkdir(libDir);
List<URL> libUrls = new ArrayList<>();
try (JarFile jarFile = new JarFile(pluginJarPath)) {
Enumeration<JarEntry> entries = jarFile.entries();
+ Yaml yaml = new Yaml();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
File file = new File(libDir, entry.getName());
- if (!entry.isDirectory() && entry.getName().endsWith(".jar")) {
+ if (entry.isDirectory()) {
+ continue;
+ }
+ if (entry.getName().endsWith(".jar")) {
if (!file.getParentFile().exists()) {
FileUtils.createParentDirectories(file);
}
@@ -335,18 +438,30 @@ public class PluginServiceImpl implements PluginService {
out.flush();
}
}
+ if ((entry.getName().contains("define")) &&
(entry.getName().endsWith(".yml") || entry.getName().endsWith(".yaml"))) {
+ try (InputStream ymlInputStream =
jarFile.getInputStream(entry)) {
+ List<ParamDefine> params = yaml.loadAs(ymlInputStream,
List.class);
+ PARAMS_DEFINE_MAP.put(pluginMetadataId, params);
+ }
+ }
}
}
return libUrls;
}
@Override
- public <T> void pluginExecute(Class<T> clazz, Consumer<T> execute) {
+ public <T> void pluginExecute(Class<T> clazz, Consumer<T> execute,
BiConsumer<T, List<Configmap>> biConsumer) {
for (URLClassLoader pluginClassLoader : pluginClassLoaders) {
ServiceLoader<T> load = ServiceLoader.load(clazz,
pluginClassLoader);
for (T t : load) {
if (pluginIsEnable(t.getClass())) {
- execute.accept(t);
+ Long pluginId =
ITEM_TO_PLUGINMETADATAID_MAP.get(t.getClass().getName());
+ List<Configmap> configmapList = PARAMS_MAP.get(pluginId);
+ if (CollectionUtils.isEmpty(configmapList)) {
+ execute.accept(t);
+ } else {
+ biConsumer.accept(t, configmapList);
+ }
}
}
}
diff --git
a/manager/src/test/java/org/apache/hertzbeat/manager/service/PluginServiceTest.java
b/manager/src/test/java/org/apache/hertzbeat/manager/service/PluginServiceTest.java
index 2fbfa72a6..59dc49b20 100644
---
a/manager/src/test/java/org/apache/hertzbeat/manager/service/PluginServiceTest.java
+++
b/manager/src/test/java/org/apache/hertzbeat/manager/service/PluginServiceTest.java
@@ -39,6 +39,7 @@ import org.apache.hertzbeat.common.entity.manager.PluginItem;
import org.apache.hertzbeat.common.entity.manager.PluginMetadata;
import org.apache.hertzbeat.manager.dao.PluginItemDao;
import org.apache.hertzbeat.manager.dao.PluginMetadataDao;
+import org.apache.hertzbeat.manager.dao.PluginParamDao;
import org.apache.hertzbeat.manager.service.impl.PluginServiceImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -60,16 +61,20 @@ class PluginServiceTest {
@InjectMocks
private PluginServiceImpl pluginService;
+
@Mock
private PluginMetadataDao metadataDao;
+ @Mock
+ private PluginParamDao pluginParamDao;
+
@Mock
private PluginItemDao itemDao;
@BeforeEach
void setUp() {
- pluginService = new PluginServiceImpl(metadataDao, itemDao);
+ pluginService = new PluginServiceImpl(metadataDao, itemDao,
pluginParamDao);
}
@Test
diff --git a/plugin/src/main/java/org/apache/hertzbeat/plugin/Plugin.java
b/plugin/src/main/java/org/apache/hertzbeat/plugin/Plugin.java
index d72632925..efeed5046 100644
--- a/plugin/src/main/java/org/apache/hertzbeat/plugin/Plugin.java
+++ b/plugin/src/main/java/org/apache/hertzbeat/plugin/Plugin.java
@@ -17,7 +17,9 @@
package org.apache.hertzbeat.plugin;
+import java.util.List;
import org.apache.hertzbeat.common.entity.alerter.Alert;
+import org.apache.hertzbeat.common.entity.job.Configmap;
/**
* Plugin
@@ -28,4 +30,10 @@ public interface Plugin {
* execute when alert
*/
void alert(Alert alert);
+
+ /**
+ * Supports user-defined parameters
+ */
+ void alert(Alert alert, List<Configmap> params);
+
}
diff --git a/plugin/src/main/resources/define/define.yml
b/plugin/src/main/resources/define/define.yml
new file mode 100644
index 000000000..25b47f6ad
--- /dev/null
+++ b/plugin/src/main/resources/define/define.yml
@@ -0,0 +1,14 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/web-app/src/app/routes/setting/plugins/plugin.component.html
b/web-app/src/app/routes/setting/plugins/plugin.component.html
index fd749ed97..4bf3bffd2 100644
--- a/web-app/src/app/routes/setting/plugins/plugin.component.html
+++ b/web-app/src/app/routes/setting/plugins/plugin.component.html
@@ -92,6 +92,9 @@
<!-- <td nzAlign="center">{{ data.enableStatus }}</td>-->
<td nzAlign="center">
<div class="actions">
+ <button nz-button nzType="primary"
(click)="onEditPluginParamDefine(data.id)" nz-tooltip
[nzTooltipTitle]="'plugin.edit' | i18n">
+ <i nz-icon nzType="edit" nzTheme="outline"></i>
+ </button>
<button
nz-button
nzType="primary"
@@ -159,3 +162,24 @@
</form>
</div>
</nz-modal>
+
+<nz-modal
+ [(nzVisible)]="isEditPluginParamDefineModalVisible"
+ [nzTitle]="'plugin.edit' | i18n"
+ (nzOnCancel)="onEditPluginParamDefineModalCancel()"
+ (nzOnOk)="onEditPluginParamDefineModalOk()"
+ nzMaskClosable="false"
+>
+ <div *nzModalContent class="-inner-content">
+ <form nz-form #form="ngForm">
+ <ng-container *ngFor="let paramDefine of paramDefines; let i = index">
+ <nz-form-item>
+ <nz-form-label nzSpan="7" [nzRequired]="paramDefine.required"
[nzFor]="paramDefine.field">{{ paramDefine.name }} </nz-form-label>
+ <nz-form-control nzSpan="8" [nzErrorTip]="'validation.required' |
i18n">
+ <app-form-field [item]="paramDefine" [name]="paramDefine.field"
[(ngModel)]="params[paramDefine.field].paramValue" />
+ </nz-form-control>
+ </nz-form-item>
+ </ng-container>
+ </form>
+ </div>
+</nz-modal>
diff --git a/web-app/src/app/routes/setting/plugins/plugin.component.ts
b/web-app/src/app/routes/setting/plugins/plugin.component.ts
index 0ac42a2ed..142a7533f 100644
--- a/web-app/src/app/routes/setting/plugins/plugin.component.ts
+++ b/web-app/src/app/routes/setting/plugins/plugin.component.ts
@@ -27,6 +27,7 @@ import { NzTableQueryParams } from 'ng-zorro-antd/table';
import { NzUploadFile } from 'ng-zorro-antd/upload';
import { finalize } from 'rxjs/operators';
+import { ParamDefine } from '../../../pojo/ParamDefine';
import { Plugin } from '../../../pojo/Plugin';
import { PluginService } from '../../../service/plugin.service';
@@ -47,8 +48,10 @@ export class SettingPluginsComponent implements OnInit {
jarFile: [null, [Validators.required]],
enableStatus: [true, [Validators.required]]
});
+ this.lang = this.i18nSvc.defaultLang;
}
+ lang: string;
pageIndex: number = 1;
pageSize: number = 8;
total: number = 0;
@@ -275,4 +278,63 @@ export class SettingPluginsComponent implements OnInit {
});
this.fileList = [];
}
+
+ params: any = {};
+ paramDefines!: ParamDefine[];
+ isEditPluginParamDefineModalVisible = false;
+
+ onEditPluginParamDefine(pluginId: number) {
+ const getPluginParamDefine$ = this.pluginService
+ .getPluginParamDefine(pluginId)
+ .pipe(
+ finalize(() => {
+ getPluginParamDefine$.unsubscribe();
+ })
+ )
+ .subscribe((message: any) => {
+ if (message.code === 0) {
+ this.paramDefines = message.data.paramDefines.map((i: any) => {
+ this.params[i.field] = {
+ pluginMetadataId: pluginId,
+ // Parameter type 0: number 1: string 2: encrypted string 3:
json string mapped by map
+ type: i.type === 'number' ? 0 : i.type === 'text' || i.type ===
'string' ? 1 : i.type === 'json' ? 3 : 2,
+ field: i.field,
+ paramValue: this.getParamValue(message.data.pluginParams,
i.field)
+ };
+ i.name = i.name[this.lang];
+ return i;
+ });
+ this.isEditPluginParamDefineModalVisible = true;
+ } else {
+ this.notifySvc.error(this.i18nSvc.fanyi('common.notify.edit-fail'),
message.msg);
+ }
+ });
+ }
+
+ onEditPluginParamDefineModalCancel() {
+ this.isEditPluginParamDefineModalVisible = false;
+ }
+
+ onEditPluginParamDefineModalOk() {
+ const savePluginParamDefine$ = this.pluginService
+ .savePluginParamDefine(Object.values(this.params))
+ .pipe(
+ finalize(() => {
+ savePluginParamDefine$.unsubscribe();
+ })
+ )
+ .subscribe((message: any) => {
+ if (message.code === 0) {
+ this.isEditPluginParamDefineModalVisible = false;
+
this.notifySvc.success(this.i18nSvc.fanyi('common.notify.edit-success'), '');
+ } else {
+ this.notifySvc.error(this.i18nSvc.fanyi('common.notify.edit-fail'),
message.msg);
+ }
+ });
+ }
+
+ getParamValue(pluginParams: any[], field: string) {
+ const pluginParam = (pluginParams || []).filter((i: any) => i.field ===
field);
+ return pluginParam.length > 0 ? pluginParam[0].paramValue : null;
+ }
}
diff --git a/web-app/src/app/service/plugin.service.ts
b/web-app/src/app/service/plugin.service.ts
index 90278805d..1b34ed5cb 100644
--- a/web-app/src/app/service/plugin.service.ts
+++ b/web-app/src/app/service/plugin.service.ts
@@ -64,20 +64,6 @@ export class PluginService {
return this.http.put<Message<any>>(plugin_uri, body);
}
- public newTags(body: Tag[]): Observable<Message<any>> {
- return this.http.post<Message<any>>(plugin_uri, body);
- }
-
- public newTag(body: Tag): Observable<Message<any>> {
- const tags = [];
- tags.push(body);
- return this.http.post<Message<any>>(plugin_uri, tags);
- }
-
- public editTag(body: Tag): Observable<Message<any>> {
- return this.http.put<Message<any>>(plugin_uri, body);
- }
-
public deletePlugins(pluginIds: Set<number>): Observable<Message<any>> {
let httpParams = new HttpParams();
pluginIds.forEach(pluginId => {
@@ -86,4 +72,17 @@ export class PluginService {
const options = { params: httpParams };
return this.http.delete<Message<any>>(plugin_uri, options);
}
+
+ public getPluginParamDefine(pluginId: number): Observable<Message<any>> {
+ let httpParams = new HttpParams();
+ httpParams = httpParams.appendAll({
+ pluginMetadataId: pluginId
+ });
+ const options = { params: httpParams };
+ return this.http.get<Message<any>>(`${plugin_uri}/params/define`, options);
+ }
+
+ public savePluginParamDefine(body: any): Observable<Message<any>> {
+ return this.http.post<Message<any>>(`${plugin_uri}/params`, body);
+ }
}
diff --git
a/web-app/src/app/shared/components/form-field/form-field.component.html
b/web-app/src/app/shared/components/form-field/form-field.component.html
index 0362b7973..8b61e8141 100644
--- a/web-app/src/app/shared/components/form-field/form-field.component.html
+++ b/web-app/src/app/shared/components/form-field/form-field.component.html
@@ -18,7 +18,7 @@
-->
<app-multi-func-input
- *ngIf="item.type === 'text' || item.type === 'array'"
+ *ngIf="item.type === 'text' || item.type === 'string' || item.type ===
'array'"
[(value)]="value"
(valueChange)="onChange($event)"
[required]="item.required"
diff --git a/web-app/src/assets/i18n/en-US.json
b/web-app/src/assets/i18n/en-US.json
index 3daee47a8..db13194e8 100644
--- a/web-app/src/assets/i18n/en-US.json
+++ b/web-app/src/assets/i18n/en-US.json
@@ -581,6 +581,7 @@
"plugin.delete": "Delete Plugin",
"plugin.type.POST_ALERT": "POST ALERT",
"plugin.search": "Search plugins",
+ "plugin.edit": "Edit plugin",
"define.help": "The monitor templates define each monitoring type, parameter
variable, metrics info, collection protocol, etc. You can select an existing
monitoring template from the drop-down menu then make modifications according
to your own needs. The bottom-left area is the compare area and the
bottom-right area is the editing place. <br> You can also click \"New Monitor
Type\" to custom define an new type. Currently supported protocols include<a
href='https://hertzbeat.apache.org/ [...]
"define.help.link":
"https://hertzbeat.apache.org/zh-cn/docs/advanced/extend-point/",
"define.save-apply": "Save And Apply",
diff --git a/web-app/src/assets/i18n/zh-CN.json
b/web-app/src/assets/i18n/zh-CN.json
index 573068280..1603c6af1 100644
--- a/web-app/src/assets/i18n/zh-CN.json
+++ b/web-app/src/assets/i18n/zh-CN.json
@@ -564,6 +564,7 @@
"plugin.type": "插件类型",
"plugin.type.POST_ALERT": "告警后",
"plugin.search": "搜索插件",
+ "plugin.edit": "编辑插件",
"define.help":
"监控模版定义每一个监控类型,类型的参数变量,指标信息,采集协议等。您可根据需求在下拉菜单中选择已有监控模板修改。左下区域为对照区,右下区域为编辑区。<br>您也可以点击“<i>新增监控类型</i>”来自定义新的的监控类型,目前支持
<a href='https://hertzbeat.apache.org/zh-cn/docs/advanced/extend-http'> HTTP
协议</a>,<a
href='https://hertzbeat.apache.org/zh-cn/docs/advanced/extend-jdbc'>JDBC协议</a>,<a
href='https://hertzbeat.apache.org/zh-cn/docs/advanced/extend-ssh'>SSH协议</a>,<a
href='https://hertzbeat.apache.org/zh-cn/docs/advanced/extend-jmx'> JMX
协议</a>,<a href='https://hertzbeat.apa [...]
"define.help.link":
"https://hertzbeat.apache.org/zh-cn/docs/advanced/extend-point/",
"define.save-apply": "保存并应用",
diff --git a/web-app/src/assets/i18n/zh-TW.json
b/web-app/src/assets/i18n/zh-TW.json
index dfdc25251..6f974a620 100644
--- a/web-app/src/assets/i18n/zh-TW.json
+++ b/web-app/src/assets/i18n/zh-TW.json
@@ -577,6 +577,7 @@
"plugin.delete": "刪除插件",
"plugin.type.POST_ALERT": "告警後",
"plugin.search": "搜尋插件",
+ "plugin.edit": "編輯插件",
"define.help":
"監控模版定義每一個監控類型,類型的參數變量,指標信息,採集協議等。您可根據需求在下拉功能表中選擇已有監控模版進行修改。右下區域為編輯區,左下區域為對照區。<br>您也可以點擊“<i>新增監控類型</i>”來自定義新的的監控類型,現支持<a
href='https://hertzbeat.apache.org/zh-cn/docs/advanced/extend-http'>
HTTP協議</a>,<a
href='https://hertzbeat.apache.org/zh-cn/docs/advanced/extend-jdbc'>JDBC協定</a>,<a
href='https://hertzbeat.apache.org/zh-cn/docs/advanced/extend-ssh'>SSH協定</a>,<a
href='https://hertzbeat.apache.org/zh-cn/docs/advanced/extend-jmx'>
JMX協定</a>,<a href='https://hertzbeat.apac [...]
"define.help.link":
"https://hertzbeat.apache.org/zh-cn/docs/advanced/extend-point/",
"define.save-apply": "保存並應用",
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]