У меня есть приложение Grails 1.3.7. Я использую классы Spring JMS для настройки одной из моих служб Grails в качестве прослушивателя сообщений, устанавливая эти классы в приложении grails-app/conf/resources.groovy. Я использую maven 2.0.9 для сборки, используя grails-maven-plugin 1.3.7 и цель «maven-war» для создания файла войны.
У меня есть два сценария:
- Я хочу иметь возможность запускать приложение Grails локально из командной строки, используя «mvn grails: run-app». Я использую это во время разработки.
- Я хочу иметь возможность запускать приложение в JBoss 5.1.0 GA, развернув военный файл, созданный maven. Это то, что мы делаем в наших средах интеграции, тестирования и производства.
При работе внутри JBoss все зависимости, связанные с поставщиком JMS, доступны и предоставляются сервером приложений. Обычный способ справиться с этим с помощью maven - объявить эти зависимости в файле pom с областью действия «предоставлено». Это сделает эти зависимости доступными для компиляции и модульных тестов, но исключит их из файла войны.
Однако, когда я запускаю локально из командной строки с помощью «mvn grails:run-app», кажется, что эти зависимости недоступны для grails во время выполнения, о чем свидетельствуют многие исключения ClassNotFound и т. д. Изменение области действия на «компилировать» позволяет мне работать локально. Однако теперь эти зависимости упакованы в мой военный файл, который мне не нужен и имеет тенденцию ломать вещи при работе внутри JBoss.
Решение (или обходной путь), которое я нашел на данный момент, состоит в том, чтобы включить эти зависимости JMS с областью действия по умолчанию (компиляция) в моем pom и удалить эти jar-файлы (и все их транзитивные зависимости) из файла войны через некоторый код в 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)
Мой вопрос: есть ли лучший способ - с помощью параметров конфигурации grail и/или maven - выполнить то, что я хочу, то есть иметь возможность успешно запускать grail локально и в JBoss без необходимости вручную исключать все транзитивные зависимости из военный файл?
Примечание. Я не могу изменить используемую версию 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)
}
}
}
пом.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>
ресурсы.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
}
}