Thursday, September 19, 2019

Post#85.Project Dependencies


Project Dependencies

Unless your project is small, your project may need external Java APIs or frameworks which are packaged in their own JAR files. These JAR files are needed on the classpath when you compile your project code.
Keeping your project up-to-date with the correct versions of these external JAR files can be a comprehensive task. Each external JAR may again also need other external JAR files etc. Downloading all these external dependencies (JAR files) recursively and making sure that the right versions are downloaded is cumbersome. Especially when your project grows big, and you get more and more external dependencies.

Luckily, Maven has built-in dependency management. You specify in the POM file what external libraries your project depends on, and which version, and then Maven downloads them for you and puts them in your local Maven repository. If any of these external libraries need other libraries, then these other libraries are also downloaded into your local Maven repository.
You specify your project dependencies inside the dependencies element in the POM file. 

Here is an example:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
   http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.jenkov.crawler</groupId>
    <artifactId>java-web-crawler</artifactId>
    <version>1.0.0</version>
   
      <dependencies>

        <dependency>
          <groupId>org.jsoup</groupId>
          <artifactId>jsoup</artifactId>
          <version>1.7.1</version>
        </dependency>

        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.8.1</version>
          <scope>test</scope>
        </dependency>

      </dependencies>
   
    <build>
    </build>
</project>

Notice the dependencies element in bold. Inside it are two dependency elements. Each dependencyelement describes an external dependency.
Each dependency is described by its groupIdartifactId and version. You may remember that this is also how you identified your own project in the beginning of the POM file. The example above needs the org.jsoup group's jsoup artifact in version 1.7.1, and the junit group's junit artifact in version 4.8.1.

When this POM file is executed by Maven, the two dependencies will be downloaded from a central Maven repository and put into your local Maven repository. If the dependencies are already found in your local repository, Maven will not download them. Only if the dependencies are missing will they be downloaded into your local repository.

Sometimes a given dependency is not available in the central Maven repository. You can then download the dependency yourself and put it into your local Maven repository. Remember to put it into a subdirectory structure matching the groupIdartifactId and version. Replace all dots (.) with / and separate the groupIdartifactId and version with / too. Then you have your subdirectory structure.
The two dependencies downloaded by the example above will be put into the following subdirectories:

MAVEN_REPOSITORY_ROOT/junit/junit/4.8.1
MAVEN_REPOSITORY_ROOT/org/jsoup/jsoup/1.7.1

External Dependencies
An external dependency in Maven is a dependency (JAR file) which is not located in a Maven repository (neiterh local, central or remote repository). It may be located somewhere on your local hard disk, for instance in the lib directory of a webapp, or somewhere else. The word "external" thus means external to the Maven repository system - not just external to the project. Most dependencies are external to the project, but few are external to the repository system (not located in a repository).

You configure an external dependency like this:

<dependency>
  <groupId>mydependency</groupId>
  <artifactId>mydependency</artifactId>
  <scope>system</scope>
  <version>1.0</version>
  <systemPath>${basedir}\war\WEB-INF\lib\mydependency.jar</systemPath>
</dependency>

The groupId and artifactId are both set to the name of the dependency.
The scope element value is set to system.
The systemPath element is set to point to the location of the JAR file containing the dependency.
The ${basedir} points to the directory where the POM is located. The rest of the path is relative from that directory.

Snapshot Dependencies
Snapshot dependencies are dependencies (JAR files) which are under development. Instead of constantly updating the version numbers to get the latest version, you can depend on a snapshot version of the project. Snapshot versions are always downloaded into your local repository for every build, even if a matching snapshot version is already located in your local repository. Always downloading the snapshot dependencies assures that you always have the latest version in your local repository, for every build.
You can tell Maven that your project is a snapshot version simply by appending -SNAPSHOT to the version number in the beginning of the POM (where you also set the groupId and artifactId). Here is a version element example:

<version>1.0-SNAPSHOT</version>

Notice the -SNAPSHOT appended to the version number.
Depending on a snapshot version is also done by appending the -SNAPSHOT after the version number when configuring dependencies. Here is an example:

<dependency>
    <groupId>com.jenkov</groupId>
    <artifactId>java-web-crawler</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

The -SNAPSHOT appended to the version number tells Maven that this is a snapshot version.
You can configure how often Maven shall download snapshot dependencies in the Maven Settings File.

Transitive Dependencies
If your project depends on a dependency, say Dependency ABC, and Dependency ABC itself depends on another dependency, say Dependency XYZ, then your project has a transitive dependency on Dependency XYZ.

Exclude Dependency
Sometimes the direct dependencies of your project may clash with the transitive dependencies of the direct dependencies. For instance, you may be using a JAX-RS implementation which internally uses an older version of the Jackson JSON Toolkit. However, your application may be using a newer version of the Jackson JSON Toolkit. How do you know which of the two versions will be used?
A solution is to specify for the JAX-RS dependency that its dependency on the older version of the Jackson JSON Toolkit should be excluded. This is also referred to as dependency exclusion.
You specify a dependency exclusion inside the declaration of the dependency which transitive dependency you want to exclude. 
Here is an example of declaring a Maven dependency exclusion:

<dependency>
  <groupId>example.jaxrs</groupId>
  <artifactId>JAX-RS-TOOLKIT</artifactId>
  <version>1.0</version>
  <scope>compile</scope>
  <exclusions>
    <exclusion>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
    </exclusion>
  </exclusions>
</dependency>

With this dependency exclusion declaration in place, whatever version of the excluded dependency that the dependency containing the exclusion is using, will be ignored during Maven's compilation of the project.

No comments:

Post a Comment