<dependency>
<groupId>sample.ProjectD</groupId>
<artifactId>ProjectD</artifactId>
<version>1.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
Unfortunately, they didn't. As a last resort, you can exclude it on your own POM for Project-A like this:
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>sample.ProjectA</groupId>
<artifactId>Project-A</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>sample.ProjectB</groupId>
<artifactId>Project-B</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>sample.ProjectD</groupId> <!-- Exclude Project-D from Project-B -->
<artifactId>Project-D</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
If you deploy Project-A to a repository, and Project-X declares a normal dependency on Project-A, will Project-D still be excluded from the classpath?
Project-X -> Project-A
The answer is Yes. Project-A has declared that it doesn't need Project-D to run, so it won't be brought in as a transitive dependency of Project-A.
Now, consider that Project-X depends on Project-Y, as in the diagram below:
Project-X -> Project-Y
-> Project-B
-> Project-D
Project-Y also has a dependency on Project-B, and it does need the features supported by Project-D. Therefore, it will NOT place an exclusion on Project-D in its dependency list. It may also supply an additional repository, from which it can resolve Project-E. In this case, it's important that Project-D is not excluded globally, since it is a legitimate dependency of Project-Y.
As another scenario, suppose the dependency you don't want is Project-E instead of Project-D. How do you exclude it? See the diagram below:
Project-A
-> Project-B
-> Project-D
-> Project-E <!-- Exclude this dependency -->
-> Project-F
-> Project C
Exclusions work on the entire dependency graph below the point where they are declared. If you want to exclude Project-E instead of Project-D, simply change the exclusion to point at Project-E, but you don't move the exclusion down to Project-D. You cannot change Project-D's POM. If you could, you would use optional dependencies instead of exclusions, or split Project-D up into multiple subprojects, each with nothing but normal dependencies.
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>sample.ProjectA</groupId>
<artifactId>Project-A</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>sample.ProjectB</groupId>
<artifactId>Project-B</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>sample.ProjectE</groupId> <!-- Exclude Project-E from Project-B -->
<artifactId>Project-E</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
Why exclusions are made on a per-dependency basis, rather than at the POM level
This is mainly to be sure the dependency graph is predictable, and to keep inheritance effects from excluding a dependency that should not be excluded. If you get to the method of last resort and have to put in an exclusion, you should be absolutely certain which of your dependencies is bringing in that unwanted transitive dependency.