Java Spring REST 控制器类作为运行时插件

问题描述 投票:0回答:1

目前我想在我的 Spring 应用程序中实现一个插件系统。这个想法是有一个主要的 spring 应用程序来监视文件夹中是否有新的 jar 文件。当我将新的 jar 文件放入文件夹中时,主应用程序应该自动提升 RestController 类以供使用,而无需停机。在插件 jar 中没有主类或类似的东西。

Java Spring 是否可以在运行时启动外部 RestController 类?

java spring plugins
1个回答
0
投票

我从 tsarenkotxt 找到了一个关于这个主题的不错的 存储库。抽象类是插件初始化类的“接口”。线程类来自监视目录的主应用程序。

KR, 黑玫瑰01

抽象类IPlugin

package eu.arrowhead.plugin.types;

import eu.arrowhead.plugin.TargetModule;
import eu.arrowhead.plugin.TargetSystem;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.http.ResponseEntity;

import java.util.UUID;

/**
 * Interface for Plugin Startclass
 */
public abstract class IPlugin {
    protected final static Logger LOG = LogManager.getLogger(IPlugin.class);

    private UUID PLUGIN_ID;

    protected final static String PLUGIN_NAME = "Testplugin";
    protected final static String PLUGIN_DESCRIPTION = "This is a Testplugin.";
    protected final static String PLUGIN_VERSION = "1";
    protected final static TargetSystem PLUGIN_TARGETSYSTEM = null;
    protected final static TargetModule PLUGIN_TARGETMODULE = null;

    /**
     * Description of the plugin
     * @return
     */
    public static String getPluginDescription() {
        return PLUGIN_DESCRIPTION;
    }

    /**
     * Name of the plugin
     * @return
     */
    public static String getPluginName() {
        return PLUGIN_NAME;
    }

    /**
     * Version of the plugin
     * @return
     */
    public static String getPluginVersion() {
        return PLUGIN_VERSION;
    }

    /**
     * Target System of the plugin
     * @return
     */
    public static TargetSystem getPluginTargetSystem() {
        return PLUGIN_TARGETSYSTEM;
    }

    /**
     * Target Module of the plugin
     * @return
     */
    public static TargetModule getPluginTargetModule() {
        return PLUGIN_TARGETMODULE;
    }

    public void setId(UUID id) {
        this.PLUGIN_ID = id;
    }

    public UUID getId() {
        return this.PLUGIN_ID;
    }

    public static ResponseEntity restTest() {
        return null;
    }

    public void beforeStart() {
    }

    public void start() {

    }

    public void beforeStop() {

    }

    public void stop() {

    }
}

线程插件加载器

@Component
public class PluginLoader extends Thread {
    protected final static Logger logger = LogManager.getLogger(PluginLoader.class);

    @Value("${sip.integrator.plugin.dir:./plugin}")
    private String pluginDirectory;

    @Autowired
    private RequestMappingHandlerMapping handlerMapping;

    private File dir;
    private boolean isFirstStart = true;

    public PluginLoader() {
    }

    /**
     * Check if given plugin directory exists, is directory and readable. If path does not
     * exists than it will create the given directory. Default: ./plugin
     * @return
     */
    private boolean createPluginDirectory() {
        if (
                Files.isDirectory(Path.of(this.pluginDirectory)) &&
                Files.exists(Path.of(this.pluginDirectory)) &&
                Files.isReadable(Path.of(this.pluginDirectory))
        )
            return true;

        try {
            Files.createDirectory(Path.of(this.pluginDirectory),
                    PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rw-rw-r--")));
            return true;
        } catch (IOException e) {
            logger.error("Cannot create plugin path: " + this.pluginDirectory);
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Create a dynamic rest endpoint
     * @return
    */
    private Object createRestHandler(String method) throws InstantiationException, IllegalAccessException {
        return new ByteBuddy()
                .subclass(Object.class)
                .name("Initializer")
                .annotateType(AnnotationDescription.Builder
                        .ofType(RestController.class)
                        .build()
                )
                .defineMethod(method, ResponseEntity.class, Modifier.PUBLIC)
                .intercept(MethodDelegation.to(IPlugin.class))
                .make()
                .load(getClass().getClassLoader())
                .getLoaded()
                .newInstance();
    }

    public void run() {
        if (this.isFirstStart) {
            this.createPluginDirectory();
            this.isFirstStart = false;
        }

        if (this.isInterrupted())
            return;

        try {
            WatchService watcher = dir.toPath().getFileSystem().newWatchService();
            WatchKey watckKey;
            List<WatchEvent<?>> events;

            dir.toPath().register(watcher, StandardWatchEventKinds.ENTRY_CREATE,/*StandardWatchEventKinds
            .ENTRY_DELETE,*/
                    StandardWatchEventKinds.ENTRY_MODIFY);
            watckKey = watcher.take();

            while (!this.isInterrupted()) {
                events = watckKey.pollEvents();

                for (WatchEvent event : events) {
                    if (!event.context().toString().endsWith(".jar")) {
                        logger.error("JUMP");
                        continue;
                    }

                    File f = new File(dir.getAbsolutePath() + "\\" + event.context().toString());
                    URLClassLoader child = new URLClassLoader(
                            new URL[] {f.toURI().toURL()},
                            this.getClass().getClassLoader()
                    );
                    logger.error("FOUND");
                    JarInputStream jarStream;
                    try {
                        jarStream = new JarInputStream(f.toURL().openStream());
                    } catch (Exception e) {
                        e.printStackTrace();
                        logger.error("NOOOOO");
                        continue;
                    }

                    try {
                        Class<?> classToLoad = Class.forName("de.sip.plugin.Initializer", true, child);

                        handlerMapping
                                .registerMapping(
                                        RequestMappingInfo.paths("/t/a")
                                                .methods(RequestMethod.GET)
                                                .produces(MediaType.ALL_VALUE)
                                                .build(),
                                        createRestHandler("restTest"),
                                        classToLoad.getMethod("restTest")
                                );
                    } catch (Exception e) {
                        e.printStackTrace();
                        logger.error("Cannot load plugin");
                        child.close();
                        jarStream.close();
                    }
                }
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.