Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions src/site/markdown/about-checksums.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ metadata. Checksums are usually laid out in repositories next to the file in que
extension telling the checksum algorithm that produced the given checksum file content. Currently,
most Maven repositories contain SHA-1 and MD5 checksums by default (they are produced by Resolver by default).

Historically, Maven Resolver used `java.security.MessageDigest` to implement checksums. So to say, secure one-way
Historically, Maven Resolver used `java.security.MessageDigest` to implement checksums. So to speak, secure one-way
hashes provided by Java Cryptography Architecture were (mis)used to implement checksums for transport integrity
validation. There is no misunderstanding here, secure hashes MAY be used as checksums, as there is quite some
overlap between checksums and hashes in general. But this simplicity comes at a price: cryptographically safe
Expand All @@ -41,17 +41,17 @@ validation" or "error detection" (aka "bit-rot detection").

## Checksum Algorithms SPI

From a technical perspective, the above written facts infer following consequences: as checksum algorithms are exposed
to the user, so one can set them via configuration, users are not prevented to ask for SHA-256 or even SHA-512, even if
these algorithms are not part of standard Maven process. Moreover, nothing prevent users (integrating
Maven Resolver) registering with Java an alternate Java Cryptography Provider and use even broader (or exotic) set
of message digests for checksums. While this is not wrong or even mistake in any case, we do consider this as
wrong use case. The notion of transport validation and secure hashes are being constantly mixed up due historical
From a technical perspective, the above written facts imply the following consequences: because checksum algorithms are exposed
to the user, one can set them via configuration, and thus users are not prevented from asking for SHA-256 or even SHA-512, even if
these algorithms are not part of standard Maven process. Moreover, nothing prevents users (integrating
Maven Resolver) registering an alternate Java Cryptography Provider and using even broader (or exotic)
message digest algorithms for checksums. While this is not wrong, we do consider this as a
bad use case. The notion of transport validation and secure hashes are being constantly mixed up due to historical
reasons explained above.

Hence, Maven Resolver team decided to make supported set of checksums more controlled. Instead of directly exposing
Hence, the Maven Resolver team decided to make the supported set of checksum algorithms more controlled. Instead of directly exposing
`MessageDigest` algorithms, we introduced an SPI around checksums. This not only prevents wrong use cases (not
exposing all supported algorithms of `MessageDigest` to users), but also makes possible to introduce real checksum
exposing all supported algorithms of `MessageDigest` to users), but also makes it possible to introduce real checksum
algorithms. Finally, the set of supported checksum algorithms remains extensible: if some required algorithm is
not provided by Resolver, it can be easily added by creating a factory component for it.

Expand Down
22 changes: 10 additions & 12 deletions src/site/markdown/api-compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,9 @@ changes using version number. We use "major.minor.patch" versioning on resolver
semantics:

* On major version change, one should NOT expect any backward compatibility.
* On minor version change, we TRY to keep backward compatibility for those "exposed" 3 modules:
API, SPI and Util. Still, there are examples when we failed to do so, usually driven by new
features.
* On minor version change, we ENSURE backward compatibility for those "exposed" 3 modules: API,
SPI and Util.
SPI and Util. Still, there are examples when we failed to do so, usually driven by new
features.
Comment on lines 73 to +75

In any of three version changes above, in areas where we do not offer guarantees, everything
can happen.
Expand All @@ -88,26 +86,26 @@ above.

## Inside of Maven

Historically, Maven 3.1 (as Maven 3.0 used resolver from different package) provided API, SPI
and Impl from its own embedded resolver, while Util, Connector, if some plugin or extension
depended on those, was resolved. This caused that a plugin may work with different versions
of API, SPI, Impl or Connector. Given Resolver had API "frozen" for too long time, this was essentially
Historically, Maven 3.1 provided API, SPI
and Impl from its own embedded resolver, while Util and Connector, if some plugin or extension
depended on them, were resolved separately. This meant that a plugin could work with different versions
of API, SPI, Impl or Connector. Because the Resolver API was "frozen" for too long a time, this was essentially
not a problem, but still weird.

This changes in Maven 3.9+: Maven starting with version 3.9.0 will provide API, SPI, Impl
This changes in Maven 3.9+: Maven starting with version 3.9.0 will provide API, SPI, Impl,
**and Util and Connector**. Reason for this change is that Impl and Connector bundled in Maven
implements things from both, API and SPI, and there was a binary incompatible change between
implement things from both API and SPI, and there was a binary incompatible change between
Resolver 1.8.0 and previous versions.

Most Resolver users should not be affected by this change.

The binary incompatible change happened in SPI class `RepositoryLayout` as part of work done for
The binary incompatible change happened in the SPI class `RepositoryLayout` as part of work done for
[MRESOLVER-230](https://issues.apache.org/jira/browse/MRESOLVER-230), and affects both, Connector
and Impl.

## Backward Compatibility Checks

To ensure backward compatibility, starting from 1.9.0 version Maven Resolver uses
To ensure backward compatibility, starting from 1.9.0 Maven Resolver uses
[JApiCmp](https://siom79.github.io/japicmp/MavenPlugin.html),
with two executions (for source and binary level checks). The plugin is enabled on 3 modules of
Resolver mentioned at page top: API, SPI and Util. For "baseline" we use version 1.8.0.
43 changes: 22 additions & 21 deletions src/site/markdown/common-misconceptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ under the License.

Due to smooth transitions from Maven2 into Maven3 (and soon
Maven4), and the fact that Maven2 plugins kept working with Maven3, maybe
even without change, there were some misconceptions crept in
even without change, some misconceptions crept in
as well. Despite the marvel of "compatibility", Maven3 resolution
differs quite much from Maven2, and the sole reason is actual improvement
in area of resolution, it became much more precise (and, due
that lost some "bad" habits present in Maven2). Here, we will try to
differs considerably from Maven2, and the sole reason is actual improvement
in area of resolution. It became much more precise (and, due
to that, lost some "bad" habits present in Maven2). Here, we will try to
enumerate some of the most common misconceptions.

## Misconception No1: How Resolver Works
Expand All @@ -36,7 +36,7 @@ dependencies. Resolver, to achieve this, internally (but these are
exposed via API as distinguished API calls as well) performs 3 steps:
"collect", "transform" and "resolve".

The "collect" step is first, where it builds the "dirty tree" (dirty graph)
The "collect" step is first, where it builds the "dirty tree" (or dirty graph)
of artifacts. It is important to remark, that in "collect" step, while
the graph is being built, Maven uses only POMs. Hence, if collecting an
Artifact that was never downloaded to your local repository, it will
Expand Down Expand Up @@ -81,8 +81,8 @@ a showcase how Resolver works.
## Misconception No2: "Test graph" Is Superset Of "Runtime graph"

**Wrong**. As can be seen from above, for runtime graph we leave out "test" scoped
dependencies. It was true in Maven2, where test graph really was superset of runtime,
this does not stand anymore in Maven3. And this have interesting consequences. Let me show an example:
dependencies. It was true in Maven2, where test graph really was a superset of runtime,
but this does not stand anymore in Maven3. And this has interesting consequences. Let me show an example:

(Note: very same scenario, as explained below for Guice+Guava would work for Jackson Databind+Core, etc.)

Expand All @@ -98,9 +98,9 @@ Assume your project is using Google Guice, so you have declared it as a dependen

All fine and dandy. At the same time, you want to avoid any use of Guava. We all know Guava is a direct dependency
of Guice. This is fine, since as we know, the best practice is to declare all dependencies your code compiles
against. By not having Guava here, analyse tools will report if code touches Guava as "undeclared dependency".
against. By not having Guava here, analysis tools will report if code touches Guava as an "undeclared dependency".

But let's go one step further: turns out, to set up your unit tests, you **do need** Guava. So what now? Nothing, just
But let's go one step further: to set up your unit tests, you **do need** Guava. So what now? Nothing, just
add it as a test dependency, so your POM looks like this:

```
Expand Down Expand Up @@ -136,17 +136,18 @@ The `dependency:tree` plugin for this project outputs this verbose tree:
[INFO] \- com.google.j2objc:j2objc-annotations:jar:1.3:test
```

And is right, this IS the "test graph" **of the project** and contains a conflict as noted by "omitted for duplicate"
This IS the "test graph" **of the project** and contains a conflict as noted by "omitted for duplicate"
and "scope not updated to compile" remarks next to Guava nodes.

So this setup results that:
* when you compile, it ensures Guava is NOT on compile classpath, so you cannot even touch it (by mistake)
* when test-compile and test-execute runs, Guava will be present on classpath, as expected

So far good, but what happens when this library is consumed downstream by someone? When it becomes used as a library?
* when you compile, Guava is NOT on compile classpath, so you cannot even touch it (by mistake)
* when test-compile and test-execute run, Guava will be present on classpath, as expected

So far so good, but what happens when this library is consumed downstream by someone? When it becomes used as a library?
Nothing, all works as expected!

When a downstream dependency declares dependency on this project, the downstream project will get this graph (from
When a downstream dependency declares a dependency on this project, the downstream project will get this graph (from
the node that is your library):

```
Expand All @@ -166,29 +167,29 @@ the node that is your library):
[INFO] \- com.google.j2objc:j2objc-annotations:jar:1.3:compile
```

So what happens here? First, revisit "How Resolver Works", there you will see that for "runtime graph" of the
So what happens here? First, revisit "How Resolver Works". There you will see that for "runtime graph" of the
dependency the "test" and "provided" scopes of the dependency artifact **are not even considered**. They are simply
omitted. Not skipped, but completely omitted, like they do not even exists. Hence, in the graph there is
omitted. Not skipped, but completely omitted, like they do not even exist. Hence, in the graph there is
**no conflict happening** (as "test" Guava is completely omitted during "collect" step). Hence, everything
goes as expected.

### Important Consequences

One, maybe not so obvious consequence can be explained with use of `maven-assembly-plugin`. Let assume you want to
One, maybe not so obvious consequence can be explained with use of `maven-assembly-plugin`. Assume you want to
assemble your module "runtime" dependencies.

If you do it from "within" of the project, for example in package phase, your packaging will be incomplete:
If you do it from "within" the project, for example in the package phase, your packaging will be incomplete.
Guava will be missing! But if you do it from "outside" of the project (i.e. subsequent module of the build, or
downstream dependency), the assembly will contain Guava as well.

This is a [Maven Assembly plugin bug](https://issues.apache.org/jira/browse/MASSEMBLY-1008), somewhat explained
in [MRESOLVER-391](https://issues.apache.org/jira/browse/MRESOLVER-391). In short, Maven Assembly plugin considers
in [MRESOLVER-391](https://issues.apache.org/jira/browse/MRESOLVER-391). In short, the Maven Assembly plugin considers
"project test graph", and then "cherry-picks runtime scoped nodes" from it, which, as we can see in this case,
is wrong. You need to build different graphs for "runtime" and "test" classpath, unlike as it was true in Maven2.
is wrong. You need to build different graphs for "runtime" and "test" classpath.
For Assembly plugin, the problem is that as Mojo, it requests "test graph", then it reads configuration
(assembly descriptor, and this is the point where it learns about required scopes), and then it "filters"
the resolved "test graph" for runtime scopes. And it is wrong, as Guava is in test scope. Instead, the plugin
should read the configuration first, and ask Resolver for "runtime graph" and filter that. In turn, this problem
does not stand with `maven-war-plugin`, as the "war" Mojo asks resolution of "compile+runtime" scope. Of course,
WAR use case is much simpler than Assembly use case is, as former always packages same scope, while Assembly receives
the WAR use case is much simpler than the Assembly use case is, as the former always packages the same scope, while Assembly receives
a complex configuration and exposes much more complex "modus operandi".
7 changes: 4 additions & 3 deletions src/site/markdown/creating-a-repository-system-session.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,18 @@ represented by an instance of
setup can be done like this:

```java
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.supplier.RepositorySystemSupplier;

...
private static RepositorySystemSession newSession( RepositorySystem system )
{
RepositorySystemSession.SessionBuilder sessionBuilder = SessionBuilderSupplier.get();
DefaultRepositorySystemSession session = new DefaultRepositorySystemSession();

LocalRepository localRepo = new LocalRepository( "target/local-repo" );
sessionBuilder.setLocalRepositoryManager( system.newLocalRepositoryManager( session, localRepo ) );
session.setLocalRepositoryManager( system.newLocalRepositoryManager( session, localRepo ) );

return session.build();
return session;
}
```
Comment on lines +38 to 45

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix correctly replaces the undefined session variable and SessionBuilderSupplier, but system.newLocalRepositoryManager(sessionBuilder, localRepo) will not compile: newLocalRepositoryManager expects a RepositorySystemSession as its first argument, and SessionBuilder does not extend RepositorySystemSession.

The SessionBuilder API provides withLocalRepositories(LocalRepository...) for this purpose, which would make the example both simpler and correct:

Suggested change
RepositorySystemSession.SessionBuilder sessionBuilder = system.createSessionBuilder();
LocalRepository localRepo = new LocalRepository( "target/local-repo" );
sessionBuilder.setLocalRepositoryManager( system.newLocalRepositoryManager( session, localRepo ) );
sessionBuilder.setLocalRepositoryManager( system.newLocalRepositoryManager( sessionBuilder, localRepo ) );
return session.build();
return sessionBuilder.build();
}
```
RepositorySystemSession.SessionBuilder sessionBuilder = system.createSessionBuilder();
LocalRepository localRepo = new LocalRepository( "target/local-repo" );
sessionBuilder.withLocalRepositories( localRepo );
return sessionBuilder.build();


Expand Down
4 changes: 2 additions & 2 deletions src/site/markdown/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ Deploying artifacts and related metadata to a (remote) repository can be achieve

The most prominent consumer of this API is probably [maven-deploy-plugin](https://maven.apache.org/plugins/maven-deploy-plugin/).

# Repository Connector
## Repository Connector

The default repository connector implementation at [`BasicRepositoryConnectorFactory`](https://github.com/apache/maven-resolver/blob/master/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java) uses a `RepositoryLayout` to calculate the URL and a `Transporter` to achieve the actual upload/write of artifacts/metadata.
The default repository connector implementation at [`BasicRepositoryConnector`](https://github.com/apache/maven-resolver/blob/master/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java) uses a `RepositoryLayout` to calculate the URL and a `Transporter` to achieve the actual upload/write of artifacts/metadata.

## Repository Layout

Expand Down
6 changes: 3 additions & 3 deletions src/site/markdown/how-resolver-works.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ The term "resolving" is a bit overloaded, but in general it involves following s

We call an artifact "resolvable" if it can be resolved from any available (local or remote) repository. To make an artifact
"resolvable" from the local repository, one needs to "install" it. To make an artifact "resolvable" from a remote repository,
one needs to "deploy" it (this is an over-simplification; publishing is new term, but it also involves deploy step).
one needs to "deploy" it (this is an over-simplification; publishing is a new term, but it also involves deploy step).
Furthermore, there are extension points like `WorkspaceReader` that can make artifacts resolvable
without installing or deploying them, but that is an integration detail (like Maven does by exposing reactor projects).

Expand All @@ -61,7 +61,7 @@ The BF collector is now the default, as it offers better performance.

One very important thing, that is constantly misunderstood, is what information is used during graph collection.
Only certain parts of the effective model are used, not the whole POM.
If I am allowed to overly simplify, only the following aspects of the effective model are used during graph collection:
If I may oversimplify, only the following aspects of the effective model are used during graph collection:
* `project/dependencies` as direct dependencies on given node
* `project/dependencyManagement/dependencies` for **subsequent** dependency management on given node
* `project/repositories` for **subsequent** repositories to be used on given node
Expand Down Expand Up @@ -114,7 +114,7 @@ In general, the steps "dependency graph collection" and "conflict resolution" ar
The "flattening" and "artifact resolving" are also usually done together, and we use for those the term "artifact resolution".
To make the story more confusing, when all the steps are performed together is also called "dependency resolution".
The Resolver API reflects this terminology and offers methods doing collection, resolution or both.
*

* The method `CollectResult collectDependencies(RepositorySystemSession session, CollectRequest request)` performs only the collection step,
as its name suggests. Hence, only the steps "collection" and "conflict resolution" are performed.
* Method `List<ArtifactResult> resolveArtifacts(RepositorySystemSession session, Collection<? extends ArtifactRequest> requests)`
Expand Down
8 changes: 4 additions & 4 deletions src/site/markdown/local-repository.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ can happen based even on origin repository (if split by remote repository
was enabled beforehand).

Example configuration with split by remote repository:
```java
```sh
$ mvn ... -Daether.enhancedLocalRepository.split \
-Daether.enhancedLocalRepository.splitRemoteRepository
```
Expand All @@ -124,9 +124,9 @@ coexist in local repository. They will land in different places, due different
prefixes.

Example configuration for branches:
```java
```sh
$ mvn ... -Daether.enhancedLocalRepository.split \
-Daether.enhancedLocalRepository.localPrefix=maven-resolver/mresolver-253
-Daether.enhancedLocalRepository.localPrefix=maven-resolver/mresolver-253 \
-Daether.enhancedLocalRepository.splitRemoteRepository
```

Expand Down Expand Up @@ -197,7 +197,7 @@ Each artifact/metadata which cannot be resolved leads to an error either classif
1. *not found* error or
2. (any) *other* error (for authentication issues, timeouts etc.)

The caching behavior for both error types can be be configured programmatically via `org.eclipse.aether.DefaultRepositorySystemSession.setResolutionErrorPolicy(...)`.
The caching behavior for both error types can be configured programmatically via `org.eclipse.aether.DefaultRepositorySystemSession.setResolutionErrorPolicy(...)`.

In case caching is enabled for any of the two classifications a Java Properties file is created/updated (with the same filename as the cached artifact in the success case would get but with the additional suffix `.lastUpdated`) in the local repository. Within that file the key `<canonical-remote-url>.error` is updated/added. Its value either contains the error message (for type 2 resolver errors) or is empty (for type 1 resolver errors).

Expand Down
Loading
Loading