# Java

SlowShield serves Maven repositories at `https://slowshield.example.com/maven/`, for Maven, Gradle, sbt and Coursier, and so for Java, Kotlin and Scala projects. Builds keep their dependencies and plugins as they are; only where they come from changes.

| Path | Serves | For |
|---|---|---|
| `/maven/all/` | Google's groups from Google Maven, everything else from Maven Central | Maven's mirror, sbt, Coursier |
| `/maven/central/` | Maven Central | Gradle `mavenCentral()` |
| `/maven/google/` | Google Maven | Gradle `google()` |
| `/maven/gradle-plugins/` | the Gradle Plugin Portal | Gradle `gradlePluginPortal()` and plugin resolution |
| `/maven/<id>/` | a repository your administrator adds (JitPack, Confluent, …) | anything else |

## Set it up

Maven and Gradle don't read environment variables for this: each needs a file in your home directory, which covers every project on the machine.

### Maven

*~/.m2/settings.xml*

```
<settings>
  <mirrors>
    <mirror>
      <id>slowshield</id>
      <!-- private repositories stay direct: *,!their-id -->
      <mirrorOf>*</mirrorOf>
      <url>https://slowshield.example.com/maven/all/</url>
    </mirror>
  </mirrors>
</settings>
```

### Gradle

*~/.gradle/init.d/slowshield.init.gradle*

```
def slowshield = [
  'https://repo.maven.apache.org/maven2': 'https://slowshield.example.com/maven/central/',
  'https://repo1.maven.org/maven2': 'https://slowshield.example.com/maven/central/',
  'https://dl.google.com/dl/android/maven2': 'https://slowshield.example.com/maven/google/',
  'https://plugins.gradle.org/m2': 'https://slowshield.example.com/maven/gradle-plugins/',
]
def rewrite = { repo ->
  if (repo instanceof MavenArtifactRepository) {
    def to = slowshield[repo.url.toString().replaceAll('/$', '')]
    if (to) { repo.url = new URI(to) }
  }
}
beforeSettings { s -> s.pluginManagement.repositories.all(rewrite) }
settingsEvaluated { s ->
  s.pluginManagement.repositories.all(rewrite)
  s.dependencyResolutionManagement.repositories.all(rewrite)
}
allprojects { p ->
  p.buildscript.repositories.all(rewrite)
  p.repositories.all(rewrite)
}
```

### sbt

*~/.sbt/repositories*

```
[repositories]
  local
  slowshield: https://slowshield.example.com/maven/all/
```

*environment*

```
export SBT_OPTS="-Dsbt.override.build.repos=true $SBT_OPTS"
```

### Coursier

*environment*

```
export COURSIER_REPOSITORIES="ivy2Local|https://slowshield.example.com/maven/all/"
```

## What changes

- **The publish time** is each file's `Last-Modified` on the repository (when the repository stored it), or when SlowShield first saw the version listed, whichever is earlier.

- **Version ranges and `latest`** pick the newest version that is old enough: newer ones are left out of `maven-metadata.xml`, and `<latest>` and `<release>` are recomputed.

- **A pin that is too new** fails with `425 Too Early`. Maven and Gradle only show an error's status line, never its body, and a `403` would read like a credentials problem:
   ```
   Could not transfer artifact org.example:lib:jar:1.4.0 from/to slowshield (https://slowshield.example.com/maven/all/):
     status code: 425, reason phrase: Too Early (425)
   ```
   Maven asks again on every build (it caches a `404`, not a `425`), so the first build after the hold ends works without `-U`.

- **Brand-new artifacts** none of whose versions is old enough are held too: Maven doesn't fail open, because brand-new artifacts are the realistic attack (typosquats, dependency confusion).

- **Malware and tampering** are refused with `451`. Every file is checked against the repository's checksums and the fingerprint SlowShield recorded the first time.

## The second layer

Maven, Gradle, sbt and Coursier have no release-age setting, so SlowShield is the only layer.

## In Docker and CI

Write the mirror into the build container's `settings.xml`. With a build argument, the same Dockerfile still builds without SlowShield when the argument is empty:

```
FROM maven:3.9-eclipse-temurin-21 AS build
ARG MAVEN_MIRROR=https://slowshield.example.com/maven/all/
RUN if [ -n "$MAVEN_MIRROR" ]; then mkdir -p /root/.m2 && printf '%s' \
      "<settings><mirrors><mirror><id>slowshield</id><mirrorOf>*</mirrorOf><url>$MAVEN_MIRROR</url></mirror></mirrors></settings>" \
      > /root/.m2/settings.xml; fi
WORKDIR /src
COPY pom.xml .
COPY src src
RUN mvn -B package
```

This one mirror also covers Maven's own plugins: a test build of a small project fetched 107 artifacts through SlowShield, plugins included. For Gradle, copy the init script above to `/root/.gradle/init.d/slowshield.gradle`. [Building images](https://slowshield.org/docs/container-builds/) covers the base image too, and how to check that nothing in a build goes around SlowShield.

## Limits

- The Gradle wrapper downloads Gradle itself from services.gradle.org, which SlowShield doesn't serve. Use a Gradle that is already installed, such as the one in the official `gradle` images, where only SlowShield is reachable.

- Private repositories stay direct with `<mirrorOf>*,!their-id</mirrorOf>`.

- Plain HTTP (a local test instance): Maven blocks `http://` repositories; the Setup page's `settings.xml` overrides that block with the mirror id `maven-default-http-blocker`.

- Block repo1.maven.org, repo.maven.apache.org, dl.google.com and plugins.gradle.org at the firewall for machines that should only use SlowShield.

---

This page as HTML: https://slowshield.org/docs/java/. All of the guide in one file: https://slowshield.org/llms-full.txt
