title: Specifying "provided" dependencies with grails under maven
body: <p>I have a grails 1.3.7 application. I am making use of Spring's JMS classses to set up one of my grails services as a message listener, setting up those classes in grails-app/conf/resources.groovy. I am using maven 2.0.9 for builds, using the grails-maven-plugin 1.3.7 and the "maven-war" goal to create a war file.</p>
  
  <p>I have two scenarios:</p>
  
  <ol>
  <li>I want to be able to run my grails app locally, from the command line, using "mvn grails:run-app". I use this during development.</li>
  <li>I want to be able to run app within JBoss 5.1.0 GA, by deploying the war file created by maven. This is what we do in our integration, test and production environments.</li>
  </ol>
  
  <p>When running inside JBoss, all of the JMS-provider-related dependencies are available and provided by the application server. The normal way of handling this with maven is to declare these dependencies in the pom file, with scope of "provided". This will make these dependencies available for compilation and unit tests, but exclude them from the war file.</p>
  
  <p>However, when I run locally from the command line using "mvn grails:run-app", it appears that these dependencies are not available to grails at run-time, as evidenced by many ClassNotFound, etc. exceptions. Changing the scope to "compile" allows me to run locally. However, now these dependencies get packaged into my war file, which I do not want and tends to break things when running inside JBoss.</p>
  
  <p>The solution (or workaround) I have found for now is to include these JMS dependencies with default (compile) scope in my pom, and remove these jars (and all their transitive dependencies) from the war file through some code in BuildConfig.groovy (see below). This works, but it's messy and error prone because I have to list every single transitive dependency (of which there are many!).</p>
  
  <p>Some other things I have tried:</p>
  
  <p>At first, I thought perhaps I could add the required JMS dependencies to BuildConfig.groovy, in the "grails.project.dependency.resolution / dependencies" section, and leave them out of the pom completely. However, this didn't work because, according to <a href="http://grails.org/doc/1.3.7/guide/3.%20Configuration.html#3.7.8%20Maven%20Integration" rel="nofollow">this link</a>, the BuildConfig dependencies section is ignored when running grails under maven.</p>
  
  <p>I also noticed the "pom true" option (mentioned in the link above) and tried to use it. However, when trying to run grails:run-app, grails throws warnings about unresolved dependencies and gives a tomcat error:</p>
  
  <pre><code>:::: 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 &lt;bootloader&gt;), have different Class objects for the type org/xml/sax/Locator used in the signature
          at org.grails.tomcat.TomcatServer.start(TomcatServer.groovy:212)
  </code></pre>
  
  <p><strong>My question:
  Is there a better way - through grails and/or maven configuration options - to accomplish what I want - i.e. to be able to run grails successfully locally and within JBoss, without having to manually exclude all transitive dependencies from the war file?</strong></p>
  
  <p>Note: I cannot change the version of grails, JBoss or maven that I am using.</p>
  
  <p>Some excerpts of relevant files:</p>
  
  <p>BuildConfig.groovy:</p>
  
  <pre><code>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 -&gt;
          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)
          }
  
      }
  }
  </code></pre>
  
  <p>pom.xml:</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
  &lt;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"&gt;
  
      . . .
  
      &lt;dependencies&gt;
  
          . . .
  
          &lt;dependency&gt;
              &lt;!-- already a dep of grails-crud; make it scope:compile for resources.groovy --&gt;
              &lt;groupId&gt;org.springframework&lt;/groupId&gt;
              &lt;artifactId&gt;spring-jms&lt;/artifactId&gt;
              &lt;version&gt;3.0.5.RELEASE&lt;/version&gt;
          &lt;/dependency&gt;
          &lt;!-- 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
          --&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;org.jboss.javaee&lt;/groupId&gt;
              &lt;artifactId&gt;jboss-jms-api&lt;/artifactId&gt;
              &lt;version&gt;1.1.0.GA&lt;/version&gt;
          &lt;/dependency&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;org.jboss.naming&lt;/groupId&gt;
              &lt;artifactId&gt;jnp-client&lt;/artifactId&gt;
              &lt;version&gt;5.0.3.GA&lt;/version&gt;
          &lt;/dependency&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;jboss.messaging&lt;/groupId&gt;
              &lt;artifactId&gt;jboss-messaging&lt;/artifactId&gt;
              &lt;version&gt;1.4.3.GA&lt;/version&gt;
          &lt;/dependency&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;org.jboss.aop&lt;/groupId&gt;
              &lt;artifactId&gt;jboss-aop&lt;/artifactId&gt;
              &lt;version&gt;2.1.1.GA&lt;/version&gt;
              &lt;classifier&gt;client&lt;/classifier&gt;
              &lt;exclusions&gt;
                  &lt;exclusion&gt;
                      &lt;!-- see http://jira.codehaus.org/browse/GROOVY-3356 --&gt;
                      &lt;groupId&gt;apache-xerces&lt;/groupId&gt;
                      &lt;artifactId&gt;xml-apis&lt;/artifactId&gt;
                  &lt;/exclusion&gt;
              &lt;/exclusions&gt;
          &lt;/dependency&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;org.jboss.remoting&lt;/groupId&gt;
              &lt;artifactId&gt;jboss-remoting&lt;/artifactId&gt;
              &lt;version&gt;2.5.3.SP1&lt;/version&gt;
          &lt;/dependency&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;jboss&lt;/groupId&gt;
              &lt;artifactId&gt;jboss-serialization&lt;/artifactId&gt;
              &lt;version&gt;1.0.3.GA&lt;/version&gt;
          &lt;/dependency&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;oswego-concurrent&lt;/groupId&gt;
              &lt;artifactId&gt;concurrent&lt;/artifactId&gt;
              &lt;version&gt;1.3.4-jboss-update1&lt;/version&gt;
          &lt;/dependency&gt;
          &lt;!-- the following two are required in order to connect to HA-JNDI --&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;org.jboss.jbossas&lt;/groupId&gt;
              &lt;artifactId&gt;jboss-as-cluster&lt;/artifactId&gt;
              &lt;classifier&gt;jboss-ha-legacy-client&lt;/classifier&gt;
              &lt;version&gt;5.1.0.GA&lt;/version&gt;
          &lt;/dependency&gt;
          &lt;dependency&gt; 
              &lt;groupId&gt;org.jboss.cluster&lt;/groupId&gt;
              &lt;artifactId&gt;jboss-ha-client&lt;/artifactId&gt;
              &lt;version&gt;1.1.1.GA&lt;/version&gt;
          &lt;/dependency&gt;
  
          &lt;!-- End dependencies for connecting to JMS --&gt;
  
      &lt;/dependencies&gt;
  
      &lt;build&gt;
          &lt;plugins&gt;
              &lt;plugin&gt;
                  &lt;groupId&gt;org.grails&lt;/groupId&gt;
                  &lt;artifactId&gt;grails-maven-plugin&lt;/artifactId&gt;
                  &lt;version&gt;1.3.7&lt;/version&gt;
                  &lt;extensions&gt;true&lt;/extensions&gt;
                  &lt;executions&gt;
                      &lt;execution&gt;
                          &lt;goals&gt;
                              &lt;goal&gt;init&lt;/goal&gt;
                              &lt;goal&gt;maven-clean&lt;/goal&gt;
                              &lt;goal&gt;validate&lt;/goal&gt;
                              &lt;goal&gt;config-directories&lt;/goal&gt;
                              &lt;goal&gt;maven-compile&lt;/goal&gt;
                              &lt;goal&gt;maven-test&lt;/goal&gt;
                              &lt;goal&gt;maven-war&lt;/goal&gt;
                              &lt;goal&gt;maven-functional-test&lt;/goal&gt;
                          &lt;/goals&gt;
                      &lt;/execution&gt;
                  &lt;/executions&gt;
              &lt;/plugin&gt;
          &lt;/plugins&gt;
      &lt;/build&gt;
  &lt;/project&gt;
  </code></pre>
  
  <p>resources.groovy:</p>
  
  <pre><code>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
      }
  }
  </code></pre>