Angeben von "bereitgestellten" Abhängigkeiten mit Grails unter Maven
-
28-10-2019 - |
Frage
Ich habe eine Grails 1.3.7-Anwendung. Ich verwende die JMS-Klassen von Spring, um einen meiner Grails-Dienste als Nachrichten-Listener einzurichten und diese Klassen in grails-app / conf / resources.groovy einzurichten. Ich verwende Maven 2.0.9 für Builds, verwende das Grails-Maven-Plugin 1.3.7 und das Ziel "Maven-War", um eine War-Datei zu erstellen.
Ich habe zwei Szenarien:
- Ich möchte meine Grails-App lokal über die Befehlszeile mit "mvn grails: run-app" ausführen können. Ich benutze dies während der Entwicklung.
- Ich möchte die App in JBoss 5.1.0 GA ausführen können, indem ich die von maven erstellte Kriegsdatei bereitstelle. Dies tun wir in unseren Integrations-, Test- und Produktionsumgebungen.
Wenn Sie in JBoss ausgeführt werden, sind alle Abhängigkeiten im Zusammenhang mit JMS-Anbietern verfügbar und werden vom Anwendungsserver bereitgestellt. Die normale Methode, dies mit maven zu behandeln, besteht darin, diese Abhängigkeiten in der pom-Datei mit dem Gültigkeitsbereich "bereitgestellt" zu deklarieren. Dadurch werden diese Abhängigkeiten für Kompilierungs- und Komponententests verfügbar, sie werden jedoch aus der War-Datei ausgeschlossen.
Wenn ich jedoch lokal über die Befehlszeile mit "mvn grails: run-app" ausführe, scheinen diese Abhängigkeiten für Grails zur Laufzeit nicht verfügbar zu sein, wie viele Ausnahmen von ClassNotFound usw. belegen. Durch Ändern des Bereichs in "Kompilieren" kann ich lokal ausgeführt werden. Jetzt werden diese Abhängigkeiten jedoch in meine Kriegsdatei gepackt, was ich nicht möchte und dazu neige, Dinge zu beschädigen, wenn ich in JBoss ausgeführt werde.
Die Lösung (oder Problemumgehung), die ich im Moment gefunden habe, besteht darin, diese JMS-Abhängigkeiten mit dem Standardbereich (Kompilieren) in meinen POM aufzunehmen und diese Jars (und alle ihre transitiven Abhängigkeiten) durch Code in BuildConfig aus der War-Datei zu entfernen .groovy (siehe unten). Dies funktioniert, ist aber chaotisch und fehleranfällig, da ich jede einzelne transitive Abhängigkeit auflisten muss (von denen es viele gibt!).
Einige andere Dinge, die ich versucht habe:
Zuerst dachte ich, ich könnte BuildConfig.groovy im Abschnitt "grails.project.dependency.resolution / dependencies" die erforderlichen JMS-Abhängigkeiten hinzufügen und sie vollständig aus dem POM herauslassen. Dies funktionierte jedoch nicht, da laut this link wird der Abschnitt BuildConfig-Abhängigkeiten ignoriert, wenn Grails unter maven ausgeführt werden.
Ich habe auch die Option "pom true" (im obigen Link erwähnt) bemerkt und versucht, sie zu verwenden. Wenn Sie jedoch versuchen, grails: run-app auszuführen, gibt grails Warnungen vor ungelösten Abhängigkeiten aus und gibt einen Tomcat-Fehler aus:
:::: WARNINGS :::::::::::::::::::::::::::::::::::::::::::::: :: UNRESOLVED DEPENDENCIES :: :::::::::::::::::::::::::::::::::::::::::::::: :: commons-collections#commons-collections;3.2.1: configuration not found in commons-collections#commons-collections;3.2.1: 'master'. It was required from org.grails.internal#load-manager-grails;1.2-SNAPSHOT compile :: org.slf4j#slf4j-api;1.5.8: configuration not found in org.slf4j#slf4j-api;1.5.8: 'master'. It was required from org.grails.internal#load-manager-grails;1.2-SNAPSHOT runtime ... java.lang.LinkageError: loader constraint violation: when resolving overridden method "org.apache.tomcat.util.digester.Digester.setDocumentLocator(Lorg/xml/sax/Locator;)V" the class loader (instance of org/codehaus/groovy/grails/cli/support/GrailsRootLoader) of the current class, org/apache/tomcat/util/digester/Digester, and its superclass loader (instance of <bootloader>), have different Class objects for the type org/xml/sax/Locator used in the signature at org.grails.tomcat.TomcatServer.start(TomcatServer.groovy:212)
Meine Frage: Gibt es eine bessere Möglichkeit - durch Grails und / oder Maven-Konfigurationsoptionen - das zu erreichen, was ich möchte - dh Grails lokal und innerhalb von JBoss erfolgreich ausführen zu können, ohne alle transitiven Abhängigkeiten manuell aus der War-Datei ausschließen zu müssen?
Hinweis: Ich kann die von mir verwendete Version von Grails, JBoss oder Maven nicht ändern.
Einige Auszüge relevanter Dateien:
BuildConfig.groovy:
grails.project.class.dir = "target/classes" grails.project.test.class.dir = "target/test-classes" grails.project.test.reports.dir = "target/test-reports" grails.project.war.file = "target/${appName}-${appVersion}.war" grails.project.dependency.resolution = { // inherit Grails' default dependencies inherits("global") { // uncomment to disable ehcache // excludes 'ehcache' } log "warn" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose' repositories { // only use our internal Archiva instance mavenRepo "http://my-company.com/archiva/repository/mirror" mavenLocal() } dependencies { // specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes eg. } //Remove own log4j and use the one supplied by JBoss instead grails.war.resources = {stagingDir -> delete file:"$stagingDir/WEB-INF/classes/log4j.properties" // logging conf done in JBoss only def files = fileScanner { fileset(dir:"$stagingDir/WEB-INF/lib"){ [ // all of the following are jms-related dependencies supplied by JBoss /* org.jboss.javaee */ "jboss-jms-api-*.jar", /* org.jboss.naming */ "jnp-client-*.jar", /* org.jboss */ "jboss-common-core-*.jar", /* org.jboss.logging */ "jboss-logging-spi-*.jar", /* jboss.messaging */ "jboss-messaging-*.jar", /* org.jboss.aop */ "jboss-aop-*.jar", /* org.apache.ant */ "ant-*.jar", /* org.apache.ant */ "ant-launcher-*.jar", /* org.jboss */ "jboss-reflect-*.jar", /* org.jboss */ "jboss-mdr-*.jar", /* qdox */ "qdox-*.jar", /* trove */ "trove-*.jar", /* org.jboss.logging */ "jboss-logging-log4j-*.jar", /* org.jboss.remoting */ "jboss-remoting-*.jar", /* jboss */ "jboss-serialization-*.jar", /* oswego-concurrent */ "concurrent-*.jar", /* org.jboss.jbossas */ "jboss-as-cluster-*-jboss-ha-legacy-client.jar", /* commons-logging */ "commons-logging-*.jar", /* org.jboss.jbossas */ "jboss-as-server-*.jar", /* sun-jaxb */ "jaxb-api-*.jar", /* org.jboss.jbossas */ "jboss-as-deployment-*.jar", /* org.jboss.javaee */ "jboss-jad-api-*.jar", /* org.jboss.security */ "jboss-security-spi-*.jar", . . . // and the other 74 transitive dependencies... ].each{ include(name:it) } } } files.each { delete(file: it) } } }
pom.xml:
<?xml version="1.0" encoding="utf-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> . . . <dependencies> . . . <dependency> <!-- already a dep of grails-crud; make it scope:compile for resources.groovy --> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> <version>3.0.5.RELEASE</version> </dependency> <!-- Note: all the remaining jms dependencies below should be 'provided' scope, but grails doesn't correctly pull them in when running locally, so we must leave them as compile scope and remove them (and their transitive dependencies) from the war file through BuildConfig.groovy --> <dependency> <groupId>org.jboss.javaee</groupId> <artifactId>jboss-jms-api</artifactId> <version>1.1.0.GA</version> </dependency> <dependency> <groupId>org.jboss.naming</groupId> <artifactId>jnp-client</artifactId> <version>5.0.3.GA</version> </dependency> <dependency> <groupId>jboss.messaging</groupId> <artifactId>jboss-messaging</artifactId> <version>1.4.3.GA</version> </dependency> <dependency> <groupId>org.jboss.aop</groupId> <artifactId>jboss-aop</artifactId> <version>2.1.1.GA</version> <classifier>client</classifier> <exclusions> <exclusion> <!-- see http://jira.codehaus.org/browse/GROOVY-3356 --> <groupId>apache-xerces</groupId> <artifactId>xml-apis</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.jboss.remoting</groupId> <artifactId>jboss-remoting</artifactId> <version>2.5.3.SP1</version> </dependency> <dependency> <groupId>jboss</groupId> <artifactId>jboss-serialization</artifactId> <version>1.0.3.GA</version> </dependency> <dependency> <groupId>oswego-concurrent</groupId> <artifactId>concurrent</artifactId> <version>1.3.4-jboss-update1</version> </dependency> <!-- the following two are required in order to connect to HA-JNDI --> <dependency> <groupId>org.jboss.jbossas</groupId> <artifactId>jboss-as-cluster</artifactId> <classifier>jboss-ha-legacy-client</classifier> <version>5.1.0.GA</version> </dependency> <dependency> <groupId>org.jboss.cluster</groupId> <artifactId>jboss-ha-client</artifactId> <version>1.1.1.GA</version> </dependency> <!-- End dependencies for connecting to JMS --> </dependencies> <build> <plugins> <plugin> <groupId>org.grails</groupId> <artifactId>grails-maven-plugin</artifactId> <version>1.3.7</version> <extensions>true</extensions> <executions> <execution> <goals> <goal>init</goal> <goal>maven-clean</goal> <goal>validate</goal> <goal>config-directories</goal> <goal>maven-compile</goal> <goal>maven-test</goal> <goal>maven-war</goal> <goal>maven-functional-test</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
resources.groovy:
import org.codehaus.groovy.grails.commons.ConfigurationHolder import org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter import org.springframework.jms.listener.DefaultMessageListenerContainer import org.springframework.jms.listener.adapter.MessageListenerAdapter import org.springframework.jms.support.destination.JndiDestinationResolver import org.springframework.jndi.JndiObjectFactoryBean import org.springframework.jndi.JndiTemplate beans = { def config = ConfigurationHolder.config jndiTemplate(JndiTemplate) { environment = config.myQueue.ctx.toProperties() // flattens a{b{c}} to 'a.b.c' } jmsFactory(JndiObjectFactoryBean) { jndiTemplate = jndiTemplate jndiName = config.myQueue.connectionFactory as String lookupOnStartup = false // need this? proxyInterface = "javax.jms.ConnectionFactory" } authJmsFactory(UserCredentialsConnectionFactoryAdapter) { targetConnectionFactory = jmsFactory username = config.app.auth.username as String password = config.app.auth.password as String } destinationResolver(JndiDestinationResolver) { jndiTemplate = jndiTemplate } jmsMessageListener(MessageListenerAdapter, ref("myMessageDrivenService")) { defaultListenerMethod = "onEventMessage" } jmsContainer(DefaultMessageListenerContainer) { connectionFactory = authJmsFactory destinationResolver = destinationResolver destinationName = config.eventQueue.queueName as String messageListener = jmsMessageListener transactionManager = ref("transactionManager") // grails' txn mgr cacheLevel = DefaultMessageListenerContainer.CACHE_CONNECTION autoStartup = false // started up in Bootstrap.groovy } }
Lösung
Die Lösung hierfür besteht darin, über ein Profil einen dynamischen Bereich für Ihre Abhängigkeit zu erstellen.
Beispiel:
<?xml version="1.0" encoding="utf-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
. . .
<properties>
<jms.deps.scope>compile</jms.deps.scope>
</properties>
<profile>
<id>build</id>
<properties>
<jms.deps.scope>provided</jms.deps.scope>
</properties>
</profile>
<dependencies>
<dependency>
<groupId>whatever</groupId>
<artifactId>whatever</artifactId>
<scope>${jms.deps.scope}</scope>
</dependency>
</dependencies>
. . .
Wenn Ihre Befehlszeile das Profil angibt:
mvn clean install war -P build
Hoffe das hilft.
Andere Tipps
Dies wurde in Grails 2.0 "behoben".Das Maven-Plugin für Grails wurde aktualisiert, sodass der "bereitgestellte" Bereich bedeutet, dass die Abhängigkeit bei lokaler Ausführung verfügbar ist, jedoch nicht in der Paketkriegsdatei enthalten ist.