Pergunta

Eu tenho um aplicativo grails 1.3.7. Estou usando as classes JMS do Spring para configurar um dos meus serviços grails como um ouvinte de mensagem, configurando essas classes em grails-app / conf / resources.groovy. Estou usando o maven 2.0.9 para compilações, usando o grails-maven-plugin 1.3.7 e o objetivo "maven-war" para criar um arquivo war.

Tenho dois cenários:

  1. Quero poder executar meu aplicativo grails localmente, na linha de comando, usando "mvn grails: run-app". Eu uso isso durante o desenvolvimento.
  2. Eu quero ser capaz de executar o aplicativo no JBoss 5.1.0 GA, implantando o arquivo war criado por maven. Isso é o que fazemos em nossos ambientes de integração, teste e produção.

Ao executar dentro do JBoss, todas as dependências relacionadas ao provedor JMS estão disponíveis e fornecidas pelo servidor de aplicativos. A maneira normal de lidar com isso com o maven é declarar essas dependências no arquivo pom, com o escopo "fornecido". Isso tornará essas dependências disponíveis para compilação e testes de unidade, mas as excluirá do arquivo war.

No entanto, quando executo localmente a partir da linha de comando usando "mvn grails: run-app", parece que essas dependências não estão disponíveis para grails em tempo de execução, conforme evidenciado por muitas exceções ClassNotFound, etc. Alterar o escopo para "compilar" me permite executar localmente. No entanto, agora essas dependências são empacotadas em meu arquivo war, o que eu não quero e tende a quebrar as coisas ao executar dentro do JBoss.

A solução (ou solução alternativa) que encontrei por agora é incluir essas dependências JMS com escopo padrão (compilar) em meu pom e remover esses jars (e todas as suas dependências transitivas) do arquivo war por meio de algum código em BuildConfig .groovy (veja abaixo). Isso funciona, mas é confuso e sujeito a erros porque eu tenho que listar cada dependência transitiva (das quais existem muitas!).

Algumas outras coisas que tentei:

A princípio, pensei que talvez pudesse adicionar as dependências JMS necessárias ao BuildConfig.groovy, na seção "grails.project.dependency.resolution / dependencies", e deixá-las fora do pom completamente. No entanto, isso não funcionou porque, de acordo com neste link , a seção de dependências BuildConfig é ignorada ao executar grails no maven.

Também notei a opção "pom true" (mencionada no link acima) e tentei usá-la. No entanto, ao tentar executar grails: run-app, grails lança avisos sobre dependências não resolvidas e dá um erro de 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)

Minha pergunta: Existe uma maneira melhor - por meio de opções de configuração grails e / ou maven - para realizar o que eu quero - ou seja, ser capaz de executar grails localmente e dentro do JBoss, sem ter que excluir manualmente todas as dependências transitivas do arquivo war?

Observação: não posso alterar a versão do grails, JBoss ou maven que estou usando.

Alguns trechos de arquivos relevantes:

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
    }
}
Foi útil?

Solução

A solução para isso é criar um escopo dinâmico para sua dependência por meio de um perfil.

Exemplo:

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

. . .

Então, quando sua linha de comando especificará o perfil:

mvn clean install war -P build

Espero que isso ajude.

Outras dicas

Isso foi "consertado" no Grails 2.0.O plugin maven para grails foi atualizado para que o escopo "fornecido" signifique que a dependência está disponível quando executado localmente, mas não está incluída no arquivo war do pacote.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top