简体   繁体   中英

Ivy dependency as provided

Problem : I need to have a lib on the eclipse classpath that should not be deployed to Tomcat. (In a maven project it would be scope provided)

Explanation:

I've setup a project with some Ivy dependencies and had to externalize a configuration as JNI (mail/session) in order to do it I had to put the mail-1.4.7.jar inside the Tomcat lib folder.

The problem is that I have a dependency that add to my classpath the javax.mail-1.5.2.jar so I change it to:

<dependency org="org.apache.logging.log4j" name="log4j-core" rev="2.2">
    <exclude org="com.sun.mail" name="javax.mail"/>
</dependency>

The problem now is that my project break (compilation errors) because of missing mail classes such as javax.mail.MessagingException

So I have to add the mail dependency but only to eclipse. I've tried some configurations as explained here from what I know from Maven behavior with no avail.

Keeping the mail dependency only in the project, breaks Tomcat, keeping it on both tomcat and project breaks project. When I manually remove it from my project lib folder (WEB-INF\\lib), after deploy the project, it works properly.

Bottom line (after deploy):

tomcatFolder                    
  |_lib
  |   |_...
  |   |_mail-1.4.7.jar
  |   |_...
  |_webapps
        |_myproject
             |_WEB-INF
                  |_lib
                     |_...
                     |_javax.mail-1.5.2.jar //need to remove it at deploy time only
                     |_...

Can't change it to maven right now. But it is in process :)

This is really a duplicate of this question:

But.. from your question I suspect you're not using ivy configuration mappings. This is unfortunate because this is the mechanism used by ivy to logically group dependencies into functional groupings, similar to how Maven maintains scopes. The following posting attempts to bridge this understanding

Furthermore you are also using Eclipse, which means that unless you're using the ivy plugin you effectively have two build mechanisms. (ivy and eclipse). I would recommend fixing your ANT build first and then look at how to maintain the Eclipse classpath second.

Example

The first section describes how configurations are declared and used in the ivy file and the second section explains how the ivy ANT tasks are used in the build logic.

ivy.xml

You should always declare ivy configurations and use these to control your classpaths. In my builds I always have at least three: compile, runtime and test. Notice how the extends attribute is used to create relationships between the configs, because runtime should also include the compile dependencies.

Adding an additional one for the provided scope jars is easy. Simple stand-alone configuration:

<ivy-module version="2.0">
    <info organisation="com.myspotontheweb" module="demo"/>

    <configurations>
        <conf name="compile"  description="Required to compile application"/>
        <conf name="runtime"  description="Additional run-time dependencies" extends="compile"/>
        <conf name="test"     description="Required for test only" extends="runtime"/>
        <conf name="provided" description="Needed for compile, but will be present on the target platform."/>
    </configurations>

    <dependencies>
        <!-- compile dependencies -->
        <dependency org="org.slf4j" name="slf4j-api" rev="1.7.5" conf="compile->default"/>

        <!-- runtime dependencies -->
        <dependency org="org.slf4j" name="slf4j-log4j12" rev="1.7.5" conf="runtime->default"/>

        <!-- test dependencies -->
        <dependency org="junit" name="junit" rev="4.11" conf="test->default"/>

        <!-- provided dependencies -->
        <dependency org="org.apache.tomcat" name="servlet-api" rev="6.0.16" conf="provided->master"/>
    </dependencies>

</ivy-module>

It's the configuration mappings that make things special. The simple explanation is that they fall into two basic types when pulling from a Maven repository:

  • conf="local_configuration->default"
  • conf="local_configuration->master"

The first means include the remote module and all its dependencies. The second means include the remote module and exclude it's dependencies. This means you don't need the following exclude trickery:

<dependency org="org.apache.logging.log4j" name="log4j-core" rev="2.2">
    <exclude org="com.sun.mail" name="javax.mail"/>
</dependency>

You simply use the following, if all you want is the log4j-core jar:

<dependency org="org.apache.logging.log4j" name="log4j-core" rev="2.2" conf="provided->master"/>

Additional notes:

  • In ivy mapping to the remote "default" configuration will pull down only the jars you need. It will exclude optional dependencies and other stuff like javadocs.
  • Sometimes "excludes" are necessary when module authors get their dependencies wrong.

build.xml

The resolve target will pull down dependencies, generate a report and create the compile and test classpaths. Note the use of configurations to determine which jar groupings should be used:

<target name="resolve" description="Use ivy to resolve classpaths">
    <ivy:resolve/>

    <ivy:report todir='${build.dir}/ivy-reports' graph='false' xml='false'/>

    <ivy:cachepath pathid="compile.path" conf="compile,provided"/>
    <ivy:cachepath pathid="test.path"    conf="test,provided"/>
</target>

These classpath references are then used by the compile target as normal:

<target name="compile" depends="resolve,resources" description="Compile code">
    <mkdir dir="${build.dir}/classes"/>
    <javac srcdir="${src.dir}" destdir="${build.dir}/classes" includeantruntime="false" debug="true" classpathref="compile.path"/>
</target>

<target name="compile-tests" depends="compile" description="Compile tests">
    <mkdir dir="${build.dir}/test-classes"/>
    <javac srcdir="${test.src.dir}" destdir="${build.dir}/test-classes" includeantruntime="false" debug="true">
        <classpath>
            <path refid="test.path"/>
            <pathelement path="${build.dir}/classes"/>
        </classpath>
    </javac>
</target>

And the test target:

<target name="test" depends="compile-tests" description="Run unit tests">
    <mkdir dir="${build.dir}/test-reports"/>
    <junit printsummary="yes" haltonfailure="yes">
        <classpath>
            <path refid="test.path"/>
            <pathelement path="${build.dir}/classes"/>
            <pathelement path="${build.dir}/test-classes"/>
        </classpath>
        <formatter type="xml"/>
        <batchtest fork="yes" todir="${build.dir}/test-reports">
            <fileset dir="${test.src.dir}">
                <include name="**/*Test*.java"/>
                <exclude name="**/AllTests.java"/>
            </fileset>
        </batchtest>
    </junit>
</target>

Lastly the ivy retrieve task is used to build the war file. Only the "runtime" configuration jars are used:

<target name="package" depends="test" description="Create the WAR file">
    <ivy:retrieve pattern="${build.dir}/lib/[artifact].[ext]" conf="runtime"/>

    <war destfile="${war.file}" webxml="${resources.dir}/web.xml">
        <fileset dir="${resources.dir}" excludes="web.xml"/>
        <lib dir="${build.dir}/lib"/>
    </war>
</target>

In conclusion the cachepath ivy task is used to create classpath references based on the ivy configurations and the retrieve task is used when assembling the war file.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM