[ 
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

        

Reply via email to