[
https://jira.codehaus.org/browse/MNG-2205?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=305822#comment-305822
]
David Boden commented on MNG-2205:
----------------------------------
For what it's worth, I no longer agree with this issue that I started 6 years
ago! I have a best-practice document which I apply to my projects and have
pasted here in this comment. Under this strategy, I always declare compile-time
dependencies in any project that has an import; statement referring to the
class. I don't rely on transitive dependencies when I'm actually using a
dependency for compilation. I think this is what the Maven authors intended,
which is why "provided" has stayed like it is for such a long time.
1. <dependencyManagement/> in parent pom
Declare the versions for all dependencies in the project's parent pom using the
<dependencyManagement/> section. You're setting the versions that will be used
if the submodules decide to declare dependencies. Using dependency management
ensures that the version of all of your dependencies is entirely consistent
across all your sub-projects. This is especially important if you're creating a
single deployment consisting of many of these sub-modules and you are going to
deploy one set of dependency jars
Declare few or no dependencies in the <dependencies/> section of the parent
pom. It's fair to assume that all your projects will depend on JUnit and a
logging framework so you may want to add those. Anything else results in
unnecessary dependencies making their way into your sub-projects.
It's not too useful to declare versions at an even higher level (e.g. the pom
that's the parent of all your projects) but in some cases it can make life
easier.
Failing to add a dependencyManagement section results in a classic problem of
scripts being generated in sub-projects which reference jar versions that don't
end up being packaged in the final assemby's lib folder.
2. Declare dependencies where you use them - don't rely on transitive
dependencies
Transitive dependencies are very useful. If you're using the public API of a
library in your code then you shouldn't have to care how its internals work.
Your sub-project should declare a dependency on the library that your code is
using; Maven will helpfully pull in the library's transitive dependencies. The
library is free to change how its internals work and what transitive
dependencies it declares for each version.
If you're using a library, you should declare it in the <dependencies/> section
of your pom. Don't rely on transitive dependencies. If your Java code (or
Spring config; that's code too) uses commons-beanutils then you should declare
that dependency. If you don't, your build is brittle; internal changes to other
components can break your component's build.
Run:
mvn dependency:analyze
This tool will tell you about artifacts that you've failed to declare
dependencies for and will also tell you if you have extra dependencies declared
that you don't need. Be careful about dependencies that you don't use in your
.java files; the dependency plugin is only scanning those. If you use a class
in Spring config then you do really need it, even if the dependency plugin is
telling you that you don't. In these cases I find it useful to make sure that
the dependency is set to <scope>runtime</scope>. That way, when I get the
dependency:analyze report telling me that these are extra dependencies I know
that I probably set them to runtime because they're used by spring config. I
ignore the runtime dependencies and only weed out the compile scope ones.
3. Use <properties/> sparingly (in the parent pom)
Declaring all dependency versions in properties and then using those properties
in the <dependencyManagement/> section is a little like the (bad) practice of
unnecessarily declaring all your Java variables right at the top of a method.
When there's a single artifact to configure, favour hardcoding the version into
the <version>1.0</version> tag.
Use properties when there are multiple artifacts to control. For example, set:
<spring.version>3.1.1.RELEASE</spring.version>
and then use:
<version>${spring.version}</version>
for artifacts like <artifactId>spring-core</artifactId>
Properties are public, not private. When you declare a property in the parent
pom, it's available anywhere in the build. Adding a property pollutes the
(build) environment.
4. Use the enforcer plugin to guard against old or unwanted dependencies
The enforcer plugin can fail your build based on duplicate classes found (the
same classes in more than one jar file on the build classpath) or can allow you
to ban old or unwanted dependencies. This is especially useful when the owner
of a library has changed the groupId or artifactId of their artifact. This
stops the Maven dependency mechanism from picking the latest version; you'll
end up with 2 similar artifacts on the classpath. For example, from Spring 2 to
Spring 3 they changed an artifactId from spring to spring-core. Without banning
the older spring dependency, it can easily transitively creep back onto your
classpath. Then when your program runs it's somewhat pot luck as to which jar
is first on the classpath and therefore which version of a class gets loaded.
5. Detect whether you're using old plugin or dependency versions
The following goals of the versions plugin can help you keep up to date with
the latest dependencies. Ideally, get your continuous integration server to
display this information.
mvn versions:display-plugin-updates
mvn versions:display-dependency-updates
> "provided" scope dependencies must be transitive
> ------------------------------------------------
>
> Key: MNG-2205
> URL: https://jira.codehaus.org/browse/MNG-2205
> Project: Maven 2 & 3
> Issue Type: Bug
> Components: Dependencies
> Reporter: David Boden
> Priority: Critical
> Fix For: 3.x / Backlog
>
> Attachments: transitivetest.zip
>
>
> A provided scope dependency can also be thought of as "compile-only".
> Project A requires Sybase JConnect on the runtime classpath. Project A
> declares a "provided" dependency on Sybase JConnect.
> Project B depends upon Project A. Project B declares a "compile" dependency
> on Project A.
> Project C depends upon Project B. Project C declares a "compile" dependency
> on Project B.
> C
> | - compile dependency
> B
> | - compile dependency
> A
> | - provided dependency
> Sybase JConnect
> So, does Project C transitively depend on Sybase JConnect. Yes, of course!
> The "provided" dependency needs to be transitive.
> Ultimately, when Project C gets deployed, Sybase JConnect needs to be
> somewhere on the runtime classpath in order for the application to function.
> It's valid for Project C to assume that Sybase JConnect is available and use
> JDBC all over the Project C code. Project C is safe to do this because it can
> happily deduce that Sybase JConnect will be there in the runtime environment
> because Project A NEEDS IT.
> I've got Use Cases all over my aggregated build which make it absolutely
> critical and common sense that provided scope dependencies are transitive.
> For the (very rare) odd case where you don't want to inherit provided
> dependencies, you can <exclude/> them.
--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators:
https://jira.codehaus.org/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira