Straßenmagie in Skripten oder was verbindet Groovy, Ivy und Maven?

    Nachdem ich mit dem Debuggen komplexer MVEL-Skripte + MavenClassloader gequält hatte, stellte ich fest, dass der Mechanismus für die dynamische Auflösung von Abhängigkeiten in Groovy enthalten ist. Darüber hinaus ist das Debuggen von Groovy-Skripten sowohl in Idea als auch in Eclipse möglich.



    Sie fragen sich möglicherweise, warum eine dynamische Abhängigkeitsauflösung erforderlich ist? Einige Dinge sind einfacher zu tun, und einige sind nur möglich.

    In der Publikation finden Sie eine funktionierende Lösung für Groovy als einzelne JAR-Datei und ein Klassenladeprogramm aus den Maven-Repositorys für eine Java-Anwendung. Erfahren Sie mehr über die Funktionen von Grape. Um nicht unbegründet zu sein und die Möglichkeiten von Trauben waren klar ...

    Ich werde ein Beispiel aus dem offiziellen Führer geben:
    @Grapes([
        @Grab(group='org.eclipse.jetty.aggregate', module='jetty-server', version='8.1.7.v20120910'),
        @Grab(group='org.eclipse.jetty.aggregate', module='jetty-servlet', version='8.1.7.v20120910'),
        @Grab(group='javax.servlet', module='javax.servlet-api', version='3.0.1')])
    import org.eclipse.jetty.server.Server
    import org.eclipse.jetty.servlet.*
    import groovy.servlet.*
    def runServer(duration) {
        def server = new Server(8080)
        def context = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS);
        context.resourceBase = "."
        context.addServlet(TemplateServlet, "*.gsp")
        server.start()
        sleep duration
        server.stop()
    }
    runServer(10000)
    

    Dieses Skript lädt Jetty Server-Artefakte von einem Remote-Repository herunter, fügt sie dem Skriptklassenpfad hinzu, erstellt eine Instanz der http-Serverklasse, fügt einen gsp-Seitenhandler hinzu (dies ist ein leistungsstarker Vorlagenmechanismus, der sich in der Rille selbst befindet), der Server startet, wartet 10 Sekunden und stoppt ihn. Das heißt Zum Zeitpunkt der Erstellung des Skripts werden diese Abhängigkeiten nicht benötigt, es wird nur der Zugriff auf die Repositorys benötigt, und das nächste Mal befinden sich die Anlegestellenabhängigkeiten bereits im lokalen Dateisystem und Sie müssen sie nicht aus dem Netzwerk herunterladen.

    Für mich ist es ein so ausgeklügelter Mechanismus, der in die Sprache selbst eingebaut ist !!!

    Um ein Skript mit einem Jetty-Server auszuführen, benötigen Sie nur die Klassen groovy und ivy provider im Klassenpfad. Die Klassen werden mit ivy aus dem Maven-Repository geladen.

    In der Wildnis der Rillen ist eine Konfiguration verborgen, die besagt, dass Sie zuerst im lokalen Dateisystem $ {user.home} /. Groovy / grapes, dann in $ {user.home} /. M2 / repository / nach Abhängigkeiten suchen und dann versuchen müssen, diese zu finden Zuerst in jcenter, dann in ibiblio und zuletzt in java.net2-Repositorys suchen
    Die gleiche Konfiguration



    Aber es gibt eine Nuance, die die weitverbreitete Verwendung von Grape verhindert - dies ist die Implementierung seines Mechanismus zur Auflösung von Abhängigkeiten von Ivy und das Fehlen von Anbieterklassen im selben Glas wie der Groove. Hier ist, wovon ich rede:
    igor @ igor-comp: ~ / dev / projects / groovy-grape-aether $ java -jar /home/igor/.m2/repository/org/codehaus/groovy/groovy-all/2.4.5/groovy-all-2.4 .5.jar ~ / dev / projects / jetty.groovy
    Gefangen: java.lang.NoClassDefFoundError: org / apache / ivy / Ivy
    java.lang.NoClassDefFoundError: org / apache / ivy / Ivy
    Auslöser: java.lang.ClassNotFoundException: org.apache.ivy.Ivy


    Diejenigen, die Ivy mit komplexen transitiven Abhängigkeiten, Versionsbereichen oder Snapshot-Versionen aus Maven-Repositorys zu verwenden versuchten, füllten mehr als eine Unebenheit.

    Es gibt solche Zeilen im Quellcode von Grape.java des groovigen Projekts
                    // by default use GrapeIvy
                    //TODO META-INF/services resolver?
                    instance = (GrapeEngine) Class.forName("groovy.grape.GrapeIvy").newInstance();
    


    Die Suche führte zu dem Spring-Boot-Projekt, das Grape unter der Haube einsetzt, jedoch auf Kosten des auf Aether implementierten Maven-Anbieters. Aether ist eine einzelne Bibliothek für den Zugriff auf Repositorys und das Veröffentlichen von Artefakten. Es wird in Maven, Nexus, m2eclipse verwendet. Es ist unwahrscheinlich, dass Ivy auf demselben Schlachtfeld mit ihr konkurrieren kann. Es wäre toll, Äther in Trauben zu verwenden!

    GrapeEngineInstaller macht fast das, was groovige Autoren dachten, als sie den TODO-Kommentar geschrieben haben - weist dem AetherGrapeEngine-Provider das Grape.instance-Feld anstelle des hartcodierten GrapeIvy-Grooves zu.
    public abstract class GrapeEngineInstaller {
    	public static void install(GrapeEngine engine) {
    		synchronized (Grape.class) {
    			try {
    				Field field = Grape.class.getDeclaredField("instance");
    				field.setAccessible(true);
    				field.set(null, engine);
    

    Und es spielt keine Rolle, dass der "schmutzige Hack" mithilfe von Reflection im Boot implementiert wird.) Die Idee der Autoren des "TODO META-INF / services resolver" -Fugens ist auch nicht die beste, insbesondere beim Modulieren der Anwendung, und ein solcher Resolver wird mit Sicherheit ein Problem für die OSGI-Umgebung sein.

    Um vollkommen glücklich zu sein, brauche ich AetherGrapeEngine ohne alle Stiefel- und Federklassen und sogar mit allen für seine Arbeit erforderlichen Ätherklassen.

    Dies führte mich zur Operation des Spring-Boot-Projekts und der Isolierung, bei der AetherGrapeEngine- und MVN- Classloader-Classloader zu einem separaten Artefakt von nur 3 MB kombiniert wurden . Diese 3 Megabyte helfen sowohl der Groove-Sprache als auch meinem AspectJ-Scripting- Projekt ! Ich teile die Ergebnisse gerne mit und hoffe, dass das Projekt auch für Sie von Nutzen ist.

    Nach der Kombination von mvn-classloader und groovy-all haben wir ein 9,8 MB großes Artefakt erhalten, das groovy-all ersetzt und es Ihnen ermöglicht, den Grape-Mechanismus in Ihrer Groovy-Anwendung mit dem AetherGrapeEngine-Abhängigkeits-Resolver zu verwenden.

    Aus dem zentralen Repository herunterladen groovy-grape-aether-2.4.5.1.jar . Es wurde nach dem Groovy-Grape-Aether- Projekt zusammengestellt .

    Wir initialisieren den SSH-Server im Groove-Skript carash.groovy:
    @Grab(group='org.crashub', module='crash.connectors.ssh', version='1.3.1')
    import org.crsh.standalone.Bootstrap
    import org.crsh.vfs.FS.Builder
    import org.crsh.vfs.spi.url.ClassPathMountFactory
    def classLoader = Bootstrap.getClassLoader();
    def classpathDriver = new ClassPathMountFactory(classLoader);
    def cmdFS = new Builder().register("classpath", classpathDriver).mount("classpath:/crash/commands/").build();
    def confFS = new Builder().register("classpath", classpathDriver).mount("classpath:/crash/").build();
    def bootstrap = new Bootstrap(classLoader, confFS, cmdFS);
    def config = new java.util.Properties();
    config.put("crash.ssh.port", "2000");
    config.put("crash.ssh.auth_timeout", "300000");
    config.put("crash.ssh.idle_timeout", "300000");
    config.put("crash.auth", "simple");
    config.put("crash.auth.simple.username", "admin");
    config.put("crash.auth.simple.password", "admin");
    bootstrap.setConfig(config);
    bootstrap.bootstrap();
    sleep 60000
    bootstrap.shutdown();
    


    Führen Sie dieses Skript aus, um den Befehl java -jar groovy-grape-aether-2.4.5.1.jar carash.groovy auszuführen
    Und wir beobachten in der Konsole, wie das Skript eine Abhängigkeit im Repository findet und funktioniert
    05.11.2015 01:01:50 org.crsh.plugin.PluginLifeCycle configureProperty
    INFO: Konfigurieren der Eigenschaft vfs.refresh_period = 1 von Eigenschaften
    05.11.2015 01:01:50 org.crsh.plugin.ServiceLoaderDiscovery getPlugins
    INFO: Geladen plugin Plugin [Typ = SSHPlugin, Schnittstelle = SSHPlugin]
    05.11.2015 01:01:50 org.crsh.plugin.ServiceLoaderDiscovery getPlugins
    INFO: Geladenes Plugin Plugin [Typ = SSHInlinePlugin, Schnittstelle = CommandPlugin]
    05.11.2015 01:01 : 50 AM org.crsh.plugin.ServiceLoaderDiscovery getPlugins
    INFO: Geladenes Plugin Plugin [Typ = KeyAuthenticationPlugin, Schnittstelle = KeyAuthenticationPlugin]
    05.11.2015 01:01:50 org.crsh.plugin.ServiceLoaderDiscovery getPlugins
    INFO: Loaded plugin Plugin[type=CRaSHShellFactory,interface=ShellFactory]
    ноя 05, 2015 1:01:50 AM org.crsh.plugin.ServiceLoaderDiscovery getPlugins
    INFO: Loaded plugin Plugin[type=GroovyLanguageProxy,interface=Language]
    ноя 05, 2015 1:01:50 AM org.crsh.plugin.ServiceLoaderDiscovery getPlugins
    INFO: Loaded plugin Plugin[type=JavaLanguage,interface=Language]
    ноя 05, 2015 1:01:50 AM org.crsh.plugin.ServiceLoaderDiscovery getPlugins
    INFO: Loaded plugin Plugin[type=ScriptLanguage,interface=Language]
    ноя 05, 2015 1:01:50 AM org.crsh.plugin.ServiceLoaderDiscovery getPlugins
    INFO: Loaded plugin Plugin[type=JaasAuthenticationPlugin,interface=AuthenticationPlugin]
    ноя 05, 2015 1:01:50 AM org.crsh.plugin.ServiceLoaderDiscovery getPlugins
    INFO: Loaded plugin Plugin[type=SimpleAuthenticationPlugin,interface=AuthenticationPlugin]
    ноя 05, 2015 1:01:50 AM org.crsh.plugin.PluginLifeCycle configureProperty
    INFO: Configuring property ssh.port=2000 from properties
    ноя 05, 2015 1:01:50 AM org.crsh.plugin.PluginLifeCycle configureProperty
    INFO: Configuring property ssh.auth_timeout=300000 from properties
    ноя 05, 2015 1:01:50 AM org.crsh.plugin.PluginLifeCycle configureProperty
    INFO: Configuring property ssh.idle_timeout=300000 from properties
    ноя 05, 2015 1:01:50 AM org.crsh.plugin.PluginLifeCycle configureProperty
    INFO: Configuring property ssh.default_encoding=UTF-8 from properties
    ноя 05, 2015 1:01:50 AM org.crsh.plugin.PluginLifeCycle configureProperty
    INFO: Configuring property auth=simple from properties
    ноя 05, 2015 1:01:50 AM org.crsh.plugin.PluginLifeCycle configureProperty
    INFO: Configuring property auth.simple.username=admin from properties
    ноя 05, 2015 1:01:50 AM org.crsh.plugin.PluginLifeCycle configureProperty
    INFO: Configuring property auth.simple.password=admin from properties
    SLF4J: Failed to load class «org.slf4j.impl.StaticLoggerBinder».
    SLF4J: Defaulting to no-operation (NOP) logger implementation
    SLF4J: See www.slf4j.org/codes.html#StaticLoggerBinder for further details.
    ноя 05, 2015 1:01:50 AM org.crsh.plugin.PluginManager getPlugins
    INFO: Initialisiertes Plugin Plugin [Typ = KeyAuthenticationPlugin, Schnittstelle = KeyAuthenticationPlugin]
    05.11.2015 01:01:50 org.crsh.plugin.PluginManager getPlugins
    INFO: Initialisiertes Plugin Plugin [Typ = JaasAuthenticationPlugin, Schnittstelle = AuthenticationPlugin
    Nov ] 01:01:50 org.crsh.plugin.PluginManager getPlugins
    INFO: Initialisiertes Plugin Plugin [Typ = SimpleAuthenticationPlugin, Schnittstelle = AuthenticationPlugin]
    05.11.2015 01:01:50 org.crsh.ssh.SSHPlugin init
    INFO: SSHD wird gebootet
    05.11.2015 01:01:50 org.crsh.plugin.PluginManager getPlugins
    INFO: Initialisiertes Plugin Plugin [Typ = GroovyLanguageProxy, Schnittstelle = Sprache]
    05.11.2015 01:01:50 org.crsh.plugin.PluginManager getPlugins
    INFO: Initialized plugin Plugin[type=JavaLanguage,interface=Language]
    ноя 05, 2015 1:01:50 AM org.crsh.plugin.PluginManager getPlugins
    INFO: Initialized plugin Plugin[type=ScriptLanguage,interface=Language]
    ноя 05, 2015 1:01:50 AM org.crsh.plugin.PluginManager getPlugins
    INFO: Initialized plugin Plugin[type=CRaSHShellFactory,interface=ShellFactory]
    ноя 05, 2015 1:01:50 AM org.crsh.ssh.term.SSHLifeCycle init
    INFO: About to start CRaSSHD
    ноя 05, 2015 1:01:50 AM org.crsh.ssh.term.SSHLifeCycle init
    INFO: CRaSSHD started on port 2000
    ноя 05, 2015 1:01:50 AM org.crsh.plugin.PluginManager getPlugins
    INFO: Initialized plugin Plugin[type=SSHPlugin,interface=SSHPlugin]
    05.11.2015 01:01:50 org.crsh.plugin.PluginManager getPlugins
    INFO: Initialisiertes Plugin Plugin [Typ = SSHInlinePlugin, Schnittstelle = CommandPlugin] 05.11.2015
    01:01:56 org.crsh.ssh.SSHPlugin destroy
    INFO: SSHD herunterfahren


    Sie können dies überprüfen, indem Sie eine Verbindung zu diesem Server herstellen: ssh admin@127.0.0.1 -p 2000



    Wir können nun die Abhängigkeiten von Maven-Repositories in unseren Groovy-Skripten verwenden. Dazu benötigen Sie nur Groovy-All-2.4.5 in Kombination mit AetherGrapeEngine im Artefakt
    com.github.igor-suhorukovgroovy-grape-aether2.4.5.1


    Im selben Artefakt befindet sich ein Klassenladeprogramm com.github.igorsuhorukov.smreed.dropship.MavenClassLoader für ein Java-Programm. Wenn es unmöglich ist, Groovy in einem Projekt zu verwenden, steht in einem Java-Projekt eine ähnliche Funktionalität mit dynamischem Laden von Klassen zur Verfügung. Aber nur dafür wird es noch bequemer zu bedienen sein
    com.github.igor-suhorukovmvn-classloader1.3

    Hier ist ein Java-Beispielcode, um die org.crsh.standalone.Bootstrap-Klasse aus dem Maven-Repository abzurufen:
    URLClassLoader sshServerClassloader= MavenClassLoader.forMavenCoordinates("org.crashub:crash.connectors.ssh:1.3.1");
    Class bootstrapClass = sshServerClassloader.loadClass("org.crsh.standalone.Bootstrap");
    


    Um zusammenzufassen, was in dem Artikel gesagt wurde: Grape ist ein integrierter Mechanismus zum dynamischen Laden von Abhängigkeiten aus Maven-Repositories. Es ist mir gelungen, aus dem Spring Boot nur den Teil zu extrahieren, der für den GrapeEngine-Anbieter erforderlich ist, und ihn mit Aether und den minimal erforderlichen Abhängigkeiten zu kombinieren. Ivy Provider mit seinen Problemen wird nicht mehr benötigt. Ich werde in meinem Projekt lieber von MVEL zu Groovy wechseln. Und ich wünsche Ihnen erfolgreiche Experimente mit Grape und einen neuen Freiheitsgrad und eine einfache Programmierung in Groovy.

    Wir haben festgestellt, dass Groovy, Ivy und Maven einen Teil der Grape-Groove-Sprache, einer Technologie zum dynamischen Verbinden von Abhängigkeiten, binden, und wir lernen, wie Grape in unseren Skripten verwendet werden kann.

    Jetzt auch beliebt: