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]


Reply via email to