Thursday, October 12, 2023

Mastering Maven Plugins and Dependency Management with Spring Boot πŸš€

  • Maven is a widely-used build automation and project management tool, known for its robust plugin system and dependency management capabilities. 
  • In this blog post, we'll delve into two vital aspects of Maven: Plugin Configuration and Dependency Management, using Spring Boot to illustrate practical examples.
    

Part 1: Maven Plugins🧩

Plugin Configuration ⚙️
  • Maven has two types of plugins:
  • Build :executed during the build process. Examples include Clean, Install, and Surefire plugins. These should be configured in the build section of the POM.
  • Reporting :executed during site generation to produce various project reports. 
  • Examples include Javadoc and Check style plugins.
  • Maven plugins provide all the useful functionalities required to execute and manage the project build.
Spring Boot Maven plugin 
  • The Spring Boot Maven Plugin provides Spring Boot support in Apache Maven, letting you package executable jar or war archives and run an application “in-place”. To use it, you must use Maven 3.2 (or later).
  • The Spring Boot Maven plugin provides many  convenient features:
  • It collects all the jars on the classpath and builds a single, runnable "ΓΌber-jar", which makes it more convenient to execute and transport your service.
  • It searches for the public static void main() method to flag as a runnable class.
  • It provides a built-in dependency resolver that sets the version number to match Spring Boot dependencies. You can override any version you wish, but it will default to Boot’s chosen set of versions.
1. Create a Spring Boot Project: 
  • Start by creating a new Spring Boot project using your favorite IDE or Spring Initializer. Make sure Maven is the chosen build tool.
2. Update the pom.xml: 
  • Open the project's pom.xml file and add the Maven Compiler Plugin configuration:

      <project> <properties> <app.profiles>local,dev</app.profiles> </properties> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <profiles>${app.profiles}</profiles> </configuration> </plugin> </plugins> </build> </project>
    • you could tune the profiles to enable when running the application as follows.

        $ mvn spring-boot:run -Dapp.profiles=test
      • The Spring Boot Plugin has the following goals:
        • spring-boot:build-image
        • spring-boot:build-image-no-fork
        • spring-boot:build-info
        • spring-boot:help
        • spring-boot:process-aot
        • spring-boot:process-test-aot
        • spring-boot:repackage
        • spring-boot:run
        • spring-boot:start
        • spring-boot:stop
        • spring-boot:test-run

      Creating Custom PluginsπŸ› ️

      • Sometimes, standard plugins don't cater to unique project requirements. That's when creating custom Maven plugins comes in handy.
      • Let's create a custom Maven plugin that generates a report of the Spring Boot application's endpoints using Spring Boot's Actuator endpoints.
      Step 1: Create a Maven Project for the Custom Plugin
      • First, create a new Maven project for your custom plugin. You can use the Maven Archetype Plugin to create a basic project structure, as mentioned earlier.

          mvn archetype:generate -DgroupId=com.example -DartifactId=my-custom-maven-plugin -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
        Step 2: Define the Maven Plugin in your POM.xml
        • Edit your project's pom.xml to define the Maven Plugin you want to create. Specify the groupId, artifactId, and other plugin details.
          <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-plugin-plugin</artifactId> <version>3.6.0</version> <configuration> <goalPrefix>custom</goalPrefix> </configuration> </plugin> </plugins> </build>
        Step 3: Implement the Mojo (Maven Plugin) for your Plugin
        • Create a Java class for your custom Maven plugin Mojo. In this example, the plugin will make an HTTP request to the Spring Boot Actuator /actuator/mappings endpoint and generate a report of the exposed endpoints.
          package com.example; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; /** * @goal generate-report * @requiresProject false*/ @Mojo(name = "generate-report") public class CustomMavenPluginMojo extends AbstractMojo { @Override public void execute() throws MojoExecutionException { try { URL url = new URL("http://localhost:8080/actuator/mappings"); // Adjust the URL to your Spring Boot application's Actuator endpoint HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); int responseCode = connection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); String inputLine; StringBuilder content = new StringBuilder(); while ((inputLine = in.readLine()) != null) { content.append(inputLine); } in.close(); getLog().info("Spring Boot Actuator Endpoints Report:"); getLog().info(content.toString()); } else { getLog().error("Failed to fetch Actuator data. HTTP Response Code: " + responseCode); } connection.disconnect(); } catch (IOException e) { getLog().error("An error occurred while fetching Actuator data: " + e.getMessage()); } } }
        Step 4: Build and Install the Plugin
        • Build your Maven project using mvn clean install. This compiles your plugin and installs it in your local repository.
        Step 5: Use the Custom Plugin in a Spring Boot Project
        • In your Spring Boot project's pom.xml, use the custom Maven plugin you've created:

            <plugins> <plugin> <groupId>com.example</groupId> <!-- Replace with your groupId --> <artifactId>my-custom-maven-plugin</artifactId> <version>1.0-SNAPSHOT</version> <!-- Use the correct version --> <executions> <execution> <goals> <goal>generate-report</goal> </goals> </execution> </executions> </plugin> </plugins>
          • Now, when you build your Spring Boot project, you can execute your custom Maven plugin using the mvn custom:generate-report command. 
          • It will fetch Actuator data and print it as a report. Adjust the CustomMavenPluginMojo logic to fit your specific requirements.

          Part 3: Dependency ManagementπŸ“¦

          • In general, we use the dependencyManagement tag to avoid repeating the version and scope tags when we define our dependencies in the dependencies tag. In this way, the required dependency is declared in a central POM file.
          • This tag consists of a dependencies tag which itself might contain multiple dependency tags. 
          • Each dependency is supposed to have at least three main tags: groupId, artifactId, and version. 
          • Let’s see an example:

              <dependencyManagement> <dependencies> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> </dependency> </dependencies> </dependencyManagement>
            • The above code just declares the new artifact commons-lang3, but it doesn’t really add it to the project dependency resource list.
            • dependency tag contains a list of dependency tags. Each dependency is supposed to have at least two main tags, which are groupId and artifactId.
            • Let’s see a quick example:

                  <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>6.0.0</version> <scope>provided</scope> </dependency>
                • The version and scope tags can be inherited implicitly if we have used the dependencyManagement tag before in the POM file:

                      <dependencies> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> </dependencies>
                    • A very common use case for this feature is a multi-module project.
                    • Imagine we have a big project which consists of different modules. Each module has its own dependencies, and each developer might use a different version for the used dependencies. Then it could lead to a mesh of different artifact versions, which can also cause difficult and hard-to-resolve conflicts.

                    Part 4: Dependency Scopes 🌐

                    • Managing dependencies is crucial for any project, and Maven provides different dependency scopes to control how dependencies are used in your application.
                    • Dependency scopes can help to limit the transitivity of the dependencies. They also modify the classpath for different build tasks. Maven has six default dependency scopes.
                    • Compile Scope (Default): 
                      • Dependencies with the compile scope are available for compilation and at runtime. They become part of your application and are packaged when you build your project. These are the most common type of dependencies.

                        <dependencies> <!-- Compile scope (default) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Provided scope --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency>
                      • Provided Scope: 
                        • Dependencies with the provided scope are available during compilation but are not included in your application's runtime classpath. They are expected to be provided by the runtime environment. 
                        • For example, servlet containers typically provide the Servlet API, so you would declare it as a provided dependency.

                          <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>6.0.0</version> <scope>provided</scope> </dependency>
                        • Runtime Scope: 
                          • Dependencies with the runtime scope are not needed for compilation but are required at runtime. They won't be available during compile-time, but they are essential when your application is running.

                            <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> <scope>runtime</scope> </dependency>
                          • Test Scope: 
                            • Dependencies with the test scope are only available during the test phase. They don't become part of your application but are used for testing purposes.

                              <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
                            • Import  Scope:
                              • It’s only available for the dependency type pom.
                              • import indicates that this dependency should be replaced with all effective dependencies declared in its POM.
                              • Here, below custom-project dependency will be replaced with all dependencies declared in custom-project’s pom.xml <dependencyManagement> section.

                                <dependency> <groupId>com.baeldung</groupId> <artifactId>custom-project</artifactId> <version>1.3.2</version> <type>pom</type> <scope>import</scope> </dependency>

                              Conclusion

                              • In this comprehensive guide, we've explored the world of Maven plugins and dependency management using Spring Boot as a practical example. 
                              • Understanding how to configure plugins, create custom ones, and manage dependencies is crucial for efficient and successful project development. 
                              • The detailed explanation of dependency scopes and exclusions should help you make informed decisions when managing dependencies in your projects.

                              You may also like

                              Kubernetes Microservices
                              Python AI/ML
                              Spring Framework Spring Boot
                              Core Java Java Coding Question
                              Maven AWS