Указание «обеспеченных» зависимостей с помощью grails в maven - PullRequest
2 голосов
/ 21 февраля 2012

У меня есть приложение Граальс 1.3.7.Я использую классы Spring JMS для настройки одной из моих служб grails в качестве прослушивателя сообщений, настраивая эти классы в grails-app / conf / resources.groovy.Я использую maven 2.0.9 для сборок, использую grails-maven-plugin 1.3.7 и цель "maven-war" для создания файла войны.

У меня есть два сценария:

  1. Я хочу иметь возможность запускать мое приложение grails локально, из командной строки, используя "mvn grails: run-app".Я использую это во время разработки.
  2. Я хочу иметь возможность запускать приложение в JBoss 5.1.0 GA, развертывая файл war, созданный maven.Это то, что мы делаем в наших средах интеграции, тестирования и производства.

При работе в JBoss все зависимости, связанные с JMS-провайдером, доступны и предоставляются сервером приложений.Обычный способ справиться с этим с помощью maven - объявить эти зависимости в pom-файле с областью «предоставлено».Это сделает эти зависимости доступными для компиляции и модульных тестов, но исключит их из файла war.

Однако, когда я запускаю локально из командной строки, используя «mvn grails: run-app», кажется, что этиЗависимости недоступны для Grails во время выполнения, что подтверждается многими исключениями ClassNotFound и т. д.Изменение области действия на «compile» позволяет мне работать локально.Тем не менее, теперь эти зависимости упакованы в мой war-файл, который я не хочу и склонен ломать вещи при работе внутри JBoss.

Решение (или обходной путь), который я нашел на данный момент, состоит в том, чтобы включить эти зависимости JMSс областью по умолчанию (compile) в моем pom, и удалите эти файлы jar (и все их транзитивные зависимости) из файла war через некоторый код в BuildConfig.groovy (см. ниже).Это работает, но это грязно и подвержено ошибкам, потому что я должен перечислить все транзитивные зависимости (которых много!).

Некоторые другие вещи, которые я пробовал:

Сначала яподумал, что, возможно, я мог бы добавить необходимые JMS-зависимости в BuildConfig.groovy в разделе «grails.project.dependency.resolution / dependencies» и полностью исключить их из pom.Однако это не сработало, потому что, согласно этой ссылке , раздел зависимостей BuildConfig игнорируется при запуске grails под maven.

Я также заметил параметр «pom true» (упомянутый вссылка выше) и попытался его использовать.Однако при попытке запустить grails: run-app, grails выдает предупреждения о неразрешенных зависимостях и выдает ошибку tomcat:

:::: 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)

Мой вопрос: есть ли лучший способ - через grails и / илиПараметры конфигурации maven - чтобы выполнить то, что я хочу - т.е. иметь возможность успешно запускать grails локально и в JBoss, без необходимости вручную исключать все переходные зависимости из файла war?

Примечание: я не могу изменитьверсия Grails, JBoss или maven, которую я использую.

Некоторые выдержки из соответствующих файлов:

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
    }
}

Ответы [ 2 ]

1 голос
/ 02 октября 2012

Это было «исправлено» в Grails 2.0. Плагин maven для grails был обновлен, поэтому «предоставляемая» область означает, что зависимость доступна при локальном запуске, но не включена в файл war-пакета.

1 голос
/ 26 апреля 2012

Решением для этого является создание динамической области видимости для вашей зависимости через профиль.

Пример:

<?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>

. . .

Тогда, когда в командной строке будет указан профиль:

mvn clean install war -P build

Надеюсь, это поможет.

...