Search

Dark theme | Light theme
Showing posts with label Maven. Show all posts
Showing posts with label Maven. Show all posts

November 3, 2024

Mastering Maven: Disable Logging Of Progress Downloading Artifacts

When Maven needs to download artifacts from a remote repository, it logs the progress of the download. This can lead to a lot of noise in the output. Luckily, we can suppress the logging of the download progress. Since Maven 3.6.1. we can use the command-line option --no-transfer-progress to disable the logging of the download progress. There is also a short version of the option: -ntp.

First look at an example where we do not disable the logging of the download progress. In the output, we see (a lot of) messages showing which artifacts are downloaded and the progress.

$ mvn package
[INFO] Scanning for projects...
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-starter-parent/3.3.5/spring-boot-starter-parent-3.3.5.pom
Downloaded from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-starter-parent/3.3.5/spring-boot-starter-parent-3.3.5.pom (13 kB at 29 kB/s)
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-dependencies/3.3.5/spring-boot-dependencies-3.3.5.pom
Downloaded from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-dependencies/3.3.5/spring-boot-dependencies-3.3.5.pom (100 kB at 1.7 MB/s)
...
[INFO]
[INFO] --------------------------< com.example:demo >--------------------------
[INFO] Building demo 0.0.1-SNAPSHOT
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-maven-plugin/3.3.5/spring-boot-maven-plugin-3.3.5.pom
Downloaded from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-maven-plugin/3.3.5/spring-boot-maven-plugin-3.3.5.pom (4.0 kB at 308 kB/s)
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-maven-plugin/3.3.5/spring-boot-maven-plugin-3.3.5.jar
Downloaded from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-maven-plugin/3.3.5/spring-boot-maven-plugin-3.3.5.jar (137 kB at 6.5 MB/s)
...

We remove the downloaded dependencies from our local repository and run the same Maven command again, but now we add the command-line option --no-transfer-progress (or the short version -ntp). We no longer have the downloading progress messages in our output:

$ mvn --no-transfer-progress package
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------< com.example:demo >--------------------------
[INFO] Building demo 0.0.1-SNAPSHOT
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
...

Instead of adding this option each time we run Maven, we can add it to the file .mvn/maven.config in our project:

--no-transfer-progress

Alternatively, since Maven 3.9.0, we can add the option to the environment variable MAVEN_ARGS.

$ export MAVEN_ARGS=-ntp 
$ mvn package
[INFO] Scanning for projects...
[INFO]
...

Written with Maven 3.9.9.

February 2, 2024

Gradle Goodness: Using Maven Toolchains Configuration For Gradle Java Toolchain Resolution

When we apply the Java plugin to our Gradle project we can configure which Java version we want to use for compiling our source code and running our tests using a toolchain configuration. The benefit of having a toolchain configuration is that we can use a different Java version for compiling and running our code than the Java version that is used by Gradle to execute the build. Gradle will look for that Java version on our local computer or download the correct version if it is not available. To search for a local Java installation Gradle will look for operating system specific locations, installations by package managers like SKDMAN! and Jabba, IntelliJ IDEA installations and Maven Toolchain specifications. Maven Toolchain specifications is an XML file describing the location of local Java installation. Each Java installation is described by a version and optional vendor it provides and the location of the installation. Maven uses this information to find the correct Java installation when the maven-toolchain-plugin is used in a Maven project. But Gradle can also utilize Maven Toolchain specifications to find local Java installations. This can be useful when we have to work on multiple projects where some use Maven and others use Gradle. We can place the Maven Toolchain specification file in our Maven home directory. This is also the default place where Gradle will look, but we can use a project property to override this location.

The following example shows a Maven toolchain configuration with three different Java versions:

<?xml version="1.0" encoding="UTF-8"?>
<toolchains>
	<toolchain>
		<type>jdk</type>
		<provides>
			<version>11</version>
			<vendor>Azul Zulu</vendor>
		</provides>
		<configuration>
			<jdkHome>C:/Users/mrhaki/tools/apps/zulu11-jdk/current</jdkHome>
		</configuration>
	</toolchain>
	<toolchain>
		<type>jdk</type>
		<provides>
			<version>17</version>
			<vendor>Azul Zulu</vendor>
		</provides>
		<configuration>
			<jdkHome>C:/Users/mrhaki/apps/zulu17-jdk/current</jdkHome>
		</configuration>
	</toolchain>
	<toolchain>
		<type>jdk</type>
		<provides>
			<version>21</version>
			<vendor>Azul Zulu</vendor>
		</provides>
		<configuration>
			<jdkHome>C:/Users/mrhaki/tools/apps/zulu-jdk/current</jdkHome>
		</configuration>
	</toolchain>
</toolchains>

In our Gradle build file we apply the java plugin and define in the toolchain configuration we want to use Java 17 for our builds:

// File: build.gradle.kts
plugins {
    java
}

java {
    toolchain {
        // Use Java 17 for building and running tests
        languageVersion = JavaLanguageVersion.of(17)
    }
}

We can now use the javaToolchains task to see the available Java installations:

$ ./gradlew javaToolchains

> Task :javaToolchains

 + Options
     | Auto-detection:     Enabled
     | Auto-download:      Enabled

  + Azul Zulu JDK 11.0.22+7-LTS
     | Location:           C:\Users\mrhaki\tools\apps\zulu11-jdk\current
     | Language Version:   11
     | Vendor:             Azul Zulu
     | Architecture:       amd64
     | Is JDK:             true
     | Detected by:        Maven Toolchains

 + Azul Zulu JDK 17.0.10+7-LTS
     | Location:           C:\Users\mrhaki\tools\apps\zulu17-jdk\current
     | Language Version:   17
     | Vendor:             Azul Zulu
     | Architecture:       amd64
     | Is JDK:             true
     | Detected by:        Current JVM

 + Azul Zulu JDK 21.0.2+13-LTS
     | Location:           C:\Users\mrhaki\tools\apps\zulu-jdk\current
     | Language Version:   21
     | Vendor:             Azul Zulu
     | Architecture:       amd64
     | Is JDK:             true
     | Detected by:        Maven Toolchains

BUILD SUCCESSFUL in 4s
1 actionable task: 1 executed

The command was run using Java 17 and we can see in the output that it is detected by Gradle as the current JVM. The Java installations for Java 11 and Java 21 are detected using Maven Toolchains.

If the location of the Maven Toolchain specification file is not in the default location, we can use the Gradle project property org.gradle.java.installations.maven-toolchain-file to specify a custom location. We can use it from the command line using the -P option or we can add it to gradle.properties in the project root directory.

$ ./gradlew javaToolchains -Porg.gradle.java.installations.maven-toolchain-file=C:/Users/mrhaki/tools/maven/toolchains.xml

...

Written with Gradle 8.5.

April 6, 2023

Mastering Maven: Adding Maven Extensions Using extensions.xml

A .mvn directory in the root of our project can contains some useful extras. For example we can set default Maven options or Java VM options when we run a Maven command. We can also define Maven extensions we want to add to the Maven classpath using the file extensions.xml in the .mvn directory. The Maven extension we want to add can be referenced using a groupId, artifactId and version inside an <extension> element. We can define one or more extensions within the parent element extensions. Once we have defined the extension and run Maven the extension is added to classpath of Maven itself.

In the following example we apply the Maven Enforcer extension to our project. We add the file extensions.xml in the .mvn directory that we first have to create:

<!-- File: .mvn/extensions.xml -->
<extensions xmlns="http://maven.apache.org/EXTENSIONS/1.1.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/EXTENSIONS/1.1.0 http://maven.apache.org/xsd/core-extensions-1.1.0.xsd">
    <!-- Add Maven Enforcer extension -->
    <extension>
        <groupId>org.apache.maven.extensions</groupId>
        <artifactId>maven-enforcer-extension</artifactId>
        <version>3.3.0</version>
    </extension>
</extensions>

This extension requires an extra configuration file with rules we want to apply to our project. The filename must be enforcer-extension.xml and placed in the .mvn directory as well. In our example we add a rule that checks that the Java version we use to run Maven is at least version 17. And a rule to check that our Maven version is at least 3.9.1:

<!-- File: .mvn/enforcer-extension.xml -->
<extension>
    <executions>
        <execution>
            <id>maven-enforcer-extension</id>
            <phase>validate</phase>
            <configuration>
                <rules>
                    <!-- Our build requires Java 17 or higher -->
                    <requireJavaVersion>
                        <version>17</version>
                    </requireJavaVersion>
                    <!-- Use Maven 3.9.1 or higher -->
                    <requireMavenVersion>
                        <version>3.9.1</version>
                    </requireMavenVersion>
                </rules>
            </configuration>
        </execution>
    </executions>
</extension>

Next we run the package command with Java version 11.0.17 and we see in the output that the Enforcer extension failed because of the required Java version rule:

$ mvn package
...
[INFO] --- enforcer:3.3.0:enforce (maven-enforcer-extension) @ sample ---
[INFO] Rule 1: org.apache.maven.enforcer.rules.version.RequireMavenVersion passed
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.777 s (Wall Clock)
[INFO] Finished at: 2023-04-06T17:19:16+02:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-enforcer-plugin:3.3.0:enforce (maven-enforcer-extension) on project sample:
[ERROR] Rule 0: org.apache.maven.enforcer.rules.version.RequireJavaVersion failed with message:
[ERROR] Detected JDK version 11.0.17 (JAVA_HOME=/Users/mrhaki/.sdkman/candidates/java/11.0.17-tem) is not in the allowed range [17,).
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
$

Written with Maven 3.9.1.

April 4, 2023

Mastering Maven: Setting Default Maven Options With maven.config

In a previous blog we learned about setting default JVM options when we run Maven commands. We can also set default Maven options that we want to apply each time we run a Maven command. All options can be defined in the file maven.config in a .mvn directory in the root of our project. Each option must be defined on a new line. This directory and file can be added to our source control so that all users that have access to the repository will use the same Maven options.

In the following example we have a file .mvn/maven.config where we want to show Maven version information for each run, use a thread per CPU core, always try to build all modules and fail at the end if there are failures and finally we set user properties that are used by the Maven compile plugin:

--show-version
--threads=1C
--fail-at-end
-Dmaven.compiler.source=17
-Dmaven.compiler.target=17
-Dencoding=UTF-8

With the above configuration we can see that options are applied when we run the verify command:

$ mvn verify
...
Apache Maven 3.9.1 (2e178502fcdbffc201671fb2537d0cb4b4cc58f8)
Maven home: /Users/mrhaki/.sdkman/candidates/maven/current
Java version: 19.0.2, vendor: Eclipse Adoptium, runtime: /Users/mrhaki/.sdkman/candidates/java/19.0.2-tem
Default locale: en_NL, platform encoding: UTF-8
OS name: "mac os x", version: "13.2.1", arch: "x86_64", family: "mac"
...
[INFO] Scanning for projects...
[INFO]
[INFO] Using the MultiThreadedBuilder implementation with a thread count of 12
...
$

Written with Maven 3.9.1.

March 26, 2023

Mastering Maven: Setting Default JVM Options Using jvm.config

In order to add default JVM options to our Maven mvn command we can define an environment variable MAVEN_OPTS. But we can also create a file jvm.config in the directory .mvn in our project root directory. On each line we define a Java option we want to apply. We can specify JVM options, but also Java system properties we want to apply each time we run the mvn command. This directory and file can be added to our source control so that all users that have access to the repository will use the same JVM options.

The following example defines some Java system properties to change the Maven logging. We also add the JVM option --show-version, so that each time we run mvn we also see the version of Java that is used to run our build. We create the file .mvn/jvm.config and the following content:

-Dorg.slf4j.simpleLogger.showDateTime=true
-Dorg.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss
-Dorg.slf4j.simpleLogger.showThreadName=true
--show-version

Now when we run mvn we see in the output the Java version displayed and each log line has a time and thread name:

$ mvn verify
openjdk 19.0.2 2023-01-17
OpenJDK Runtime Environment Temurin-19.0.2+7 (build 19.0.2+7)
OpenJDK 64-Bit Server VM Temurin-19.0.2+7 (build 19.0.2+7, mixed mode, sharing)
22:12:35 [main] [INFO] Scanning for projects...
...
22:12:39 [main] [INFO] ------------------------------------------------------------------------
22:12:39 [main] [INFO] BUILD SUCCESS
22:12:39 [main] [INFO] ------------------------------------------------------------------------
22:12:39 [main] [INFO] Total time:  3.844 s
22:12:39 [main] [INFO] Finished at: 2023-03-26T22:12:39+02:00
22:12:39 [main] [INFO] ------------------------------------------------------------------------
$

Written with Maven 3.9.1

November 18, 2022

Mastering Maven: Exclude Modules From Build

When we are working with a multi-module project in Maven we might want to exclude a module when we invoke a build command. We might only be interested in partially building some modules. We can use the command line option -pl or --projects to specify a list of modules that need to be in our build. But we can also use ! followed by the module name to exclude modules from our build.

In the following example we have multi-module project with 4 modules: root with name parent-project, core, service and webapp:

$ pwd
parent-project
$ tree -L 1
.
├── core
├── pom.xml
├── service
└── webapp
$

First we run verify for the complete project and we see in the output that the reactor build order contains all modules:

$ mvn verify
...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] parent-project                                                     [pom]
[INFO] core                                                               [jar]
[INFO] service                                                            [jar]
[INFO] webapp                                                             [war]
[INFO]
...
$

Suppose we want to exclude our webapp module than we use $ mvn -pl '!webapp' verify. In the output we see the reactor build order doesn't include our webapp module:

$ mvn verify -pl '!webapp'
...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] parent-project                                                     [pom]
[INFO] core                                                               [jar]
[INFO] service                                                            [jar]
[INFO]
...
$

As our list of modules is not so big in this sample project we could also have used the option -pl to only specify the modules we want to build separated by a comma. We must use . to indicate our root module:

$ mvn verify -pl '.,core,service'
...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] parent-project                                                     [pom]
[INFO] core                                                               [jar]
[INFO] service                                                            [jar]
[INFO]
...
$

Written with Maven 3.8.6.

October 6, 2020

Mastering Maven: Replace Files In Archive

In a previous post we learned how to replace a file in an archive with Gradle. In this blog post we use Maven to achieve the same goal. With Maven we first have to extract the contents of the archive and then assemble a new archive where we use a file replacement to replace an original file from the archive. To extract the contents we use the task unzip with the maven-antrun-plugin. To create a new archive we use the maven-assembly-plugin. The destination directory of the extracted contents is the input fileset for the assembly definition together with the files we want to overwrite. The end result is a new archive with replaced files.

In the following pom.xml we configure the maven-antrun-plugin and maven-assembly-plugin:

<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>mrhaki</groupId>
    <artifactId>sample</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <configuration>
                            <target>
                                <!-- We unzip the original archive to target/archive-contents -->
                                <unzip src="lib/sample.zip" dest="target/archive-contents"/>
                            </target>
                        </configuration>
                        <goals>
                            <goal>run</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.3.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <!-- Name of new archive -->
                    <finalName>new-sample</finalName>
                    <!-- We don't want the id of the assembly descriptor in the filename -->
                    <appendAssemblyId>false</appendAssemblyId>
                    <descriptors>
                        <!-- Descriptor of the contents of the new archive -->
                        <descriptor>src/assembly/sample.xml</descriptor>
                    </descriptors>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

The descriptor of the assembly plugin is sample.xml and here we define the contents of the new archive:

<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
    <id>sample</id>
    <formats>
        <format>zip</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    <fileSets>
        <fileSet>
            <!-- Files that are extracted from the original archive -->
            <directory>target/archive-contents</directory>
            <outputDirectory/>
        </fileSet>
    </fileSets>
    <files>
        <file>
            <!-- New files that replace files from the original archive -->
            <source>src/main/resources/README</source>
            <outputDirectory/>
        </file>
    </files>
</assembly>

Written with Maven 3.6.3.

September 22, 2010

Grassroots Groovy: Configure Logback with Groovy

Sometimes we have to work on a project with only Java source files. But we can introduce some small Groovy stuff at grassroots level if we use Logback as a logging framework. Logback supports configuration by a Groovy script file. This means we can define the configuration with Groovy code instead of the default XML.

The following sample is a very small Maven project for a Java application. We use the Maven exec plugin so we can run the application directly from the command-line. We add the necessary dependencies to the pom.xml so we can write the Logback configuration in Groovy code. We must add the Groovy JAR file so Logback can execute the configuration file.

<?xml version="1.0"?>
<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.mrhaki</groupId>
    <artifactId>simple-app</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>java</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>com.mrhaki.java.Simple</mainClass>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>0.9.24</version>
        </dependency>
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>1.7.5</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
</project>

Our Java class is simple, just as the name suggests. We only want to demonstrate the different log levels that are used and how we can use that in our Logback configuration.

// File: src/main/java/com/mrhaki/java/Simple.java
package com.mrhaki.java;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Simple {
    private static final Logger LOGGER = LoggerFactory.getLogger(Simple.class);

    public Simple() {}

    public String sayHelloTo(String name) {
        return "Hello, " + name;
    }

    public static void main(String[] args) {
        LOGGER.info("Starting application.");
        Simple app = new Simple();
        String hello = app.sayHelloTo("mrhaki");
        LOGGER.debug("Saying: " + hello);
        LOGGER.info("Application ends.");
    }
}

Now it is time to look at the Groovy Logback configuration. Logback provides a simple DSL to configure loggers, appenders and other Logback configuration elements.

// File: src/main/resources/logback.groovy
import static ch.qos.logback.classic.Level.*
import ch.qos.logback.core.ConsoleAppender
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.core.status.OnConsoleStatusListener
import ch.qos.logback.core.FileAppender
import com.mrhaki.java.logsupport.SmileyConverter

displayStatusOnConsole()
scan('5 minutes')  // Scan for changes every 5 minutes.
setupAppenders()
setupLoggers()

def displayStatusOnConsole() {
    statusListener OnConsoleStatusListener 
}

def setupAppenders() {
    def logfileDate = timestamp('yyyy-MM-dd') // Formatted current date.
    // hostname is a binding variable injected by Logback.
    def filePatternFormat = "%d{HH:mm:ss.SSS} %-5level [${hostname}] %logger - %msg%n"
    appender('logfile', FileAppender) {
        file = "simple.${logfileDate}.log"
        encoder(PatternLayoutEncoder) {
            pattern = filePatternFormat
        }
    }

    // Add custom converter for %smiley pattern.
    conversionRule 'smiley', SmileyConverter

    appender('systemOut', ConsoleAppender) {
        encoder(PatternLayoutEncoder) {
            pattern = "%-5level %-12logger{12} %smiley - %msg%n"
        }
    }
}

def setupLoggers() {
    logger 'com.mrhaki.java.Simple', getLogLevel(), ['logfile']
    root DEBUG, ['systemOut']
}

def getLogLevel() {
    (isDevelopmentEnv() ? DEBUG : INFO)
}

def isDevelopmentEnv() {
    def env =  System.properties['app.env'] ?: 'DEV'
    env == 'DEV'
}

Logback looks for a file named logback.groovy in the classpath of the application. If the file is found it is parsed by a GroovyShell instance and Logback is configured.

We start by using the statusListener() method (line 14) so Logback logs the configuration details to the console.

To add a new appender, we use the appender() method. We use a closure to configure the appender. Loggers are configured with the logger() and root() methods. We can define the loglevel and a list of appenders that need to be used.

At line 9 we use the scan() method to define the scan interval used by Logback to check for changes in logback.groovy. If the file has changed, the new configuration is used.

At the bottom of the configuration file we define the method isDevelopmentEnv() to see if the application is running in development mode. If so we set the log level to DEBUG, otherwise it is set to INFO (see methods getLogLevel() and setupLoggers()). And because the configuration is a Groovy script this is very easy to achieve.

With the conversionRule() method at line 29 we assign the SmileyConverter class to the %smiley variable in the encoding pattern. The code for the SmileyConverter is here:

// File: src/main/java/com/mrhaki/java/logsupport/SmileyConverter.java
package com.mrhaki.java.logsupport;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.pattern.ClassicConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;

import static ch.qos.logback.classic.Level.*;

public class SmileyConverter extends ClassicConverter {
    @Override
    public String convert(ILoggingEvent event) {
        return getSmiley(event.getLevel());
    }

    // Return a different smiley for different log levels.
    private String getSmiley(Level level) {
        String result = "";
        switch (level.toInt()) {
            case DEBUG_INT:
                result = ";)";
                break;
            case INFO_INT:
                result = ":)";
                break;
            default:
                break;
        }
        return result;
    }
}

We are ready to run the application and see our logging configuration at work:

$ mvn -q compile exec:java

11:01:52,087 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@6fa8bd74 - Added status listener of type [ch.qos.logback.core.status.OnConsoleStatusListener]
11:01:52,107 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@6fa8bd74 - Setting ReconfigureOnChangeFilter scanning period to 5 minutes
11:01:52,108 |-INFO in ch.qos.logback.classic.turbo.ReconfigureOnChangeFilter@14a97f68 - Will scan for changes in file [/Users/mrhaki/Projects/mrhaki.com/blog/posts/samples/logback/target/classes/logback.groovy] every 300 seconds. 
11:01:52,109 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@6fa8bd74 - Adding ReconfigureOnChangeFilter as a turbo filter
11:01:52,170 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@6fa8bd74 - About to instantiate appender of type [ch.qos.logback.core.FileAppender]
11:01:52,171 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@6fa8bd74 - Naming appender as [logfile]
11:01:52,311 |-INFO in ch.qos.logback.core.FileAppender[logfile] - File property is set to [simple.2010-09-21.log]
11:01:52,329 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@6fa8bd74 - registering conversion word smiley with class [com.mrhaki.java.logsupport.SmileyConverter]
11:01:52,380 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@6fa8bd74 - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
11:01:52,382 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@6fa8bd74 - Naming appender as [systemOut]
INFO  c.m.j.Simple :) - Starting application.
DEBUG c.m.j.Simple ;) - Saying: Hello, mrhaki
INFO  c.m.j.Simple :) - Application ends.

First we see the results from Logback, because we used the statusListener() method. Then at the bottom we see three output lines from our Simple java code. Now let's run the application again, but this time we use a system property to set the environment to a non-development one:

$ mvn -q -Dapp.env=TEST compile exec:java

11:04:01,588 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@563b100c - Added status listener of type [ch.qos.logback.core.status.OnConsoleStatusListener]
11:04:01,607 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@563b100c - Setting ReconfigureOnChangeFilter scanning period to 5 minutes
11:04:01,609 |-INFO in ch.qos.logback.classic.turbo.ReconfigureOnChangeFilter@364e50ee - Will scan for changes in file [/Users/mrhaki/Projects/mrhaki.com/blog/posts/samples/logback/target/classes/logback.groovy] every 300 seconds. 
11:04:01,610 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@563b100c - Adding ReconfigureOnChangeFilter as a turbo filter
11:04:01,655 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@563b100c - About to instantiate appender of type [ch.qos.logback.core.FileAppender]
11:04:01,656 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@563b100c - Naming appender as [logfile]
11:04:01,811 |-INFO in ch.qos.logback.core.FileAppender[logfile] - File property is set to [simple.2010-09-21.log]
11:04:01,832 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@563b100c - registering conversion word smiley with class [com.mrhaki.java.logsupport.SmileyConverter]
11:04:01,837 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@563b100c - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
11:04:01,841 |-INFO in ch.qos.logback.classic.gaffer.ConfigurationDelegate@563b100c - Naming appender as [systemOut]
INFO  c.m.j.Simple :) - Starting application.
INFO  c.m.j.Simple :) - Application ends.

No if we look at the last two lines we see the DEBUG message is not shown anymore. That is because we changed the log level accordingly to the sytem property app.env..

As we can see introducing Groovy into a Java project is easy if we use Logback. We only have to add the Groovy JAR as a dependency and we are ready to go. And once that Groovy JAR dependency is in the project definition... (grin)

November 19, 2009

Gradle Goodness: Deploying Our Gradle Plugin to a Maven Repository

We have created a Gradle plugin, but now we want to share it with others. Suppose we have a central Maven repository within our company than we can use this repository to distribute our plugin among other developers. We don't need to change the code of our plugin, but we do need to move the code into a new project. We created the plugin in the buildSrc directory and that means it is only available for that project. We create a new directory and move the src directory from the buildSrc directory to the new project directory. Next we create a build.gradle file.

$ mkdir makedirs-plugin
$ cd makedirs-plugin
$ cp -R <old-dir>/buildSrc/src .
$ touch build.gradle

We open build.gradle in a text editor. Our plugin code is now nothing more or less than Groovy code, so we need the Groovy plugin for our project. The classes must be compiled into class files, but we have dependencies on the Gradle API for our plugin. When the files where in the buildSrc directory all was well, because Gradle would make sure the classpath was correct. But now we are a normal Groovy project, so we must define the location of the Gradle API class files ourselves. We add the lib directory of our Gradle installation directory to the groovy configuration to resolve the dependencies.

Next we define the location of our company Maven repository with the uploadArchives() method. A special repositories.mavenDeployer() method gives us the possibility to define the location. The uploadArchives task uses this location to deploy the JAR with all classes to the repository.

// File: build.gradle
usePlugin 'groovy'  // Need it to compile plugin classes.
usePlugin 'maven' // Need it to deploy to Maven repository.

// Project configuration is used for Maven deploy:
version = '1.0'
group = 'com.mrhaki.gradle.plugins'

uploadArchives {
    repositories.mavenDeployer {
        repository url: 'file:///shared/repos/m2/repository'
    }
}

dependencies {
    // Add all JAR files in the lib directory of theGradle installation directory
    // to the groovy configuration (is also used by compile configuration)
    groovy fileTree(dir: new File(gradle.gradleHomeDir, 'lib'), includes: ['*.jar'])
}

// Make sure all code is compiled, tested and checked before uploadArchives.
uploadArchives.dependsOn ':build'

We run the build to deploy the JAR file to the repository: $ gradle uploadArchives.

Okay we have deployed the plugin to the repository, it is time to use it in another project. We create a new directory and create the build.gradle file and open it in a text editor. We must use the usePlugin() method to enable the plugin, but the com.mrhaki.blog.gradle.MakeDirsPlugin class is not yet on the classpath. We need to define the build script dependency on the plugin with the buildscript() method. This will make sure any dependency needed by the build process itself can be resolved.

// File: build.gradle
buildscript {
    repositories {
        mavenRepo urls: 'file:///shared/repos/m2/repository'
    }
    dependencies {
        classpath 'com.mrhaki.gradle.plugins:makedirs-plugin:1.0'
    }
}

usePlugin com.mrhaki.blog.gradle.MakeDirsPlugin

Written with Gradle 0.8.

Gradle Goodness: Use Our Local Maven Repo with Gradle

Usually when we work on a software component or library we package the files into a JAR file. If we use Maven we run the $ mvn install command to copy the JAR artifact into our local Maven repository, so we can use the component or library in other project on our local machine. We can do the same thing with Gradle, we run $ gradle install to copy the artifact into the local Maven repository. The only thing we need to do is use the Maven plugin in our project. Gradle knows the location of our Maven repository, because Gradle uses the Maven ANT tasks to implement the install task.

To use the local Maven repository in our Gradle projects for dependency resolution we must do something extra. The local Maven repository is not added to the repositories list of our project automatically, we must do this ourselves. In our build.gradle file we define a new Maven repository and point it to our local Maven repository. Now Gradle uses this repository to look for dependencies just like Maven does.

// File: build.gradle
usePlugin 'java'  // Java plugin to build our JAR artifact.
usePlugin 'maven'  // Maven plugin to install artifact in local Maven repo.

def localMavenRepo = 'file://' + new File(System.getProperty('user.home'), '.m2/repository').absolutePath
repositories {
    // Use local Maven repo location. We don't need this if we only want to install
    // an artifact, but we do need it if we want to use dependencies from the local
    // repository.
    mavenRepo urls: localMavenRepo
}

// Project configuration:
version = '1.0-SNAPSHOT'
group = 'com.mrhaki.gradle'

// The following line is not necessary. Default the install tasks depends on the
// jar task, but this means no tests and checks are executed when we use the
// install task. The following line makes the install tasks depend on the build task
// and now all tests and checks are done before install is executed.
install.dependsOn ':build'

To deploy our artifact to the Maven local repository we run the install command. If we use the -i argument to get extra information we see at the end where Gradle is installing the JAR file.

$ gradle install -i
...
Tasks to be executed: [task: ':compileJava', task: ':processResources', task: ':classes', task: ':jar', task: ':assemble', task: ':compileTestJava', task: ':processTestResources', task: ':testClasses', task: ':test', task: ':check', task: ':build', task: ':install']
...
[INFO] Installing .../build/libs/project-1.0-SNAPSHOT.jar to .../.m2/repository/com/mrhaki/gradle/project/1.0-SNAPSHOT/project-1.0-SNAPSHOT.jar

BUILD SUCCESSFUL

Total time: 2.625 secs

Written with Gradle 0.8.

August 23, 2009

Grassroots Groovy: the Maven Route (part 2)

We can introduce Groovy into our projects at grassroots level. In a a previous blog we learned how we could add Groovy code to a Maven based Java project. In this blog post we see how we can use the GMaven plugin to add Groovy code to the Maven build process itself. Sometimes we want a bit more functionality than Maven supports by default. We could write our own Maven plugin to get this extra functionality, but sometimes we can get away with a little scripting. And for this scripting we use Groovy of course.

To execute Groovy code from Maven we only have to define the GMaven plugin in our POM. And then we write Groovy code in the POM itself to be executed, or from a local file, of from an URL. The Groovy code can access a couple of variables:

  • project: points to the Maven project and we can access everything.
  • pom: just another name for the project variable.
  • session: the current Maven session.
  • settings: the user's global settings.
  • log: the SLF4J logger instance to log messages to the console.
  • ant: a reference to an AntBuilder instance to execute Ant tasks.
  • fail(): a helper method to throw a MojoExecutionException.

Let's add the Maven plugin to our POM with a reference to a local Groovy script file:

<?xml version="1.0"?>
<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mrhaki.sample</groupId>
    <artifactId>sample-app</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>sample-app</name>
    <url>http://maven.apache.org</url>
    <build>
        <plugins>
            <!-- GMaven plugin so we can run Groovy scripts. -->
            <plugin>
                <groupId>org.codehaus.groovy.maven</groupId>
                <artifactId>gmaven-plugin</artifactId>
                <version>1.0</version>
                <executions>
                    <execution>
                        <!-- Script will be executed in 
                                the generate-resources phase. -->
                        <phase>generate-resources</phase>
                        <goals>
                            <goal>execute</goal>
                        </goals>
                        <configuration>
                            <source>${pom.basedir}/src/main/scripts/MavenHelper.groovy</source>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <profiles>
        <!-- Extra profile to be used in Groovy script as an example. -->
        <profile>
            <id>dev</id>
        </profile>
    </profiles>
</project>

Next we create the Groovy script file in src/main/scripts. In this script we look for active profiles, and if we have a match for a profile with id dev we log some extra info and use the Antbuilder to copy a properties file:

// Get list of active profiles.
def profiles = project.getActiveProfiles()

profiles.each{ profile ->
    // Show off Groovy's switch for matching on strings.
    switch(profile.id) {
        case 'dev':
            // Show some extra logging info.
            log.info "*" * 70
            log.info "* You are running in DEVELOPMENT mode."
            log.info "* Version: ${pom.version}, Finalname: ${pom.build.finalName}"
            log.info "*" * 70
            
            // Using Antbuilder to copy properties file.
            ant.copy(file: 'src/main/resources/dev.properties', tofile: project.build.outputDirectory + '/app.properties')
            break;
        default:
            break;
    }
}

To execute Maven with the dev profile we run the following command: $ mvn install -Pdev. In the logging generated by Maven we get the following output:

[INFO] [groovy:execute {execution: default}]
[INFO]  **********************************************************************
[INFO]  * You are running in DEVELOPMENT mode.
[INFO]  * Version: 1.0-SNAPSHOT, Finalname: sample-app-1.0-SNAPSHOT
[INFO]  **********************************************************************
Copying 1 file to P:\samples\sample-app\target\classes

So we learned how to add Groovy scripts to our Maven build. The Groovy website contains more information about all the configuration we can do to use the GMaven plugin like this.

August 22, 2009

Grassroots Groovy: the Maven Route (part 1)

We can introduce Groovy into our projects at grassroots level. In a series of blog posts we learn different routes we can follow to get our Java projects more Groovy. First off we look at how we can add Groovy sources to our Maven based Java project. But we will also see how XML handling, unit testing, Spring integration and other routes allow us to add Groovyness to a Java project in other posts.

The GMaven plugin adds Groovy support to a Maven based Java project. We configure the plugin to allow Groovy classes to be compiled just like normal Java classes. The compiled Groovy classes can than be integrated with the Java classes.

Let's start with the Maven quickstart archetype to create a simple Java Maven project:

$ mvn archetype:generate -DgroupId=com.mrhaki.sample -DartifactId=sample-app -DinteractiveMode=false
$ cd sample-app

Maven creates a new directory sample-app with a basic Java application at src/main/java/com/mrhaki/sample/App.java. We open pom.xml to add the GMaven plugin so we can compile and use our Groovy code:

<?xml version="1.0"?>
<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mrhaki.sample</groupId>
    <artifactId>sample-app</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>sample-app</name>
    <url>http://maven.apache.org</url>
    <build>
        <plugins>
            <!-- GMaven plugin so we can compile Groovy sources. -->
            <plugin>
                <groupId>org.codehaus.groovy.maven</groupId>
                <artifactId>gmaven-plugin</artifactId>
                <version>1.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>generateStubs</goal>
                            <goal>compile</goal>
                            <goal>generateTestStubs</goal>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <!-- Extra dependency for Groovy libraries for runtime. -->
        <dependency>
            <groupId>org.codehaus.groovy.maven.runtime</groupId>
            <artifactId>gmaven-runtime-1.6</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

Now our Groovy sources ending with .groovy in the directory src/main/groovy are compiled into target/classes and Groovy sources in the directory src/test/groovy are compiled into the target/test-classes directory when we run mvn install. We create a Groovy source file at src/main/groovy/com/mrhaki/sample/MyGroovy.groovy:

package com.mrhaki.sample

class MyGroovy {

    String username
    
    def reverseName() {
        username.reverse()
    }
    
    def calculateBigDecimals() {
        40G + 2G
    }
}

For this example we add a new test method to the existing AppTest class in the directory src/test/java/com/mrhaki/sample. In this test method we create an instance of the Groovy class and invoke the methods to test them:

...
public void testGroovy() {
    MyGroovy groovy = new MyGroovy();
    groovy.setUsername("mrhaki");
    assertEquals("mrhaki", groovy.getUsername());
    assertEquals("ikahrm", groovy.reverseName());
    assertEquals(new java.math.BigInteger("42"), groovy.calculateBigDecimals());
}
...

Now we run $ mvn test and our Groovy class is compiled and the Java test class will be executed without errors. So we see integrating Groovy with Java is not that difficult when we already have a Maven based Java project. In a following post we learn how we can use Groovy in the Maven POM as another route to get Groovy into our Java project.

May 25, 2009

Add CSS link in page header for Maven project site

Using Maven's site plugin is very powerful. It is so easy to generate a site with links to documentation and write our own documentation in APT or XDoc format. To keep a consistent look-and-feel it is a good idea to use a CSS file and define the look-and-feel of the site in this file. If we create the file src/site/resources/css/site.css Maven will automatically include it. In the generated pages we find a reference to the site.css:

<style type="text/css" media="all">
      @import url("./css/maven-base.css");
      @import url("./css/maven-theme.css");
      @import url("./css/site.css");
</style>

Notice the relative reference that is used. This means subprojects or submodules of our Mavenized application cannot reach this file. One way to deal with this is to write our own Maven site skin. We can create the look-and-feel in the site skin and use it among the several submodules.

But what if the company already provides a skin we must use? We cannot use more than one skin, so we need another way out. We add a reference to site.css in our HTML head section which can be recognized by all submodules. In the src/site/site.xml file we add the following:

<body>
  <head>
    <link rel="stylesheet" href="/css/site.css" type="text/css" />
  </head>
</body>

Now all generated pages will have the <link> tag in the HTML head section.

May 19, 2009

Development JAR signing in Maven

For development purposes we needed to use a signed JAR in our project. First we created a keystore:

$ keytool -genkey -alias applet -keyalg RSA -keystore src/main/keystore/signing-jar.keystore -storepass applet -keypass applet -dname "CN=domain"

We can than use the following Maven POM file definition:

...
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <executions>
        <execution>
            <goals>
                <goal>sign</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <keystore>src/main/keystore/signing-jar.keystore</keystore>
        <alias>applet</alias>
        <storepass>applet</storepass>
        <verify>true</verify>
    </configuration>
</plugin>
...

If we run mvn package we get a signed JAR file. Of course this is only useful for development purposes. To disable the JAR signing we invoke mvn package -Dmaven.jar.sign.skip=true

Include JSON-lib dependency in a Maven project

To include the JSON-lib library as a dependency to our Maven project we must not forget to use the classifier element. Otherwise the library will not be added to our project, because it cannot be downloaded from the repositories.

<dependency>
  <groupId>net.sf.json-lib</groupId>
  <artifactId>json-lib</artifactId>
  <version>2.2.3</version>
  <classifier>jdk15</classifier>
</dependency>

May 13, 2009

Configure Maven Jetty plugin for SSL communication

For a recent project I had to enable SSL communication for the Maven Jetty plugin. So when we run mvn jetty:run we must be able to use the https protocol. After browsing several mailing list I found the answer. For development we can create our own security certificate and configure the plugin to use it.

To create the development certificate we run the following command:

$ keytool -genkey -alias jetty6 -keyalg RSA -keystore target/jetty-ssl.keystore -storepass jetty6 -keypass jetty6 -dname "CN=your name or domain"

Fill in your name or domain for the -dname "CN=" option. We need the keystore and key password again when we configure the plugin in the Maven POM. The following code fragment shows how the Jetty plugin supports SSL:

<plugin>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>maven-jetty-plugin</artifactId>
    <configuration>
        <contextPath>/context</contextPath>
        <scanIntervalSeconds>5</scanIntervalSeconds>
        <connectors>
            <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
                <port>8080</port>
                <maxIdleTime>60000</maxIdleTime>
            </connector>
            <connector implementation="org.mortbay.jetty.security.SslSocketConnector">
                <port>8443</port>
                <maxIdleTime>60000</maxIdleTime>
                <keystore>${project.build.directory}/jetty-ssl.keystore</keystore>
                <password>jetty6</password>
                <keyPassword>jetty6</keyPassword>
            </connector>
        </connectors>
    </configuration>
</plugin>

In the connectors element we have defined connectors for http listening on port 8080, and for https listening on port 8443. At line 14 we reference the keystore file we have created with keytool. Lines 15, 16 define the password value.

To test this configuration we can invoke mvn jetty:run and open a web browser with address https://localhost:8443/context. We must not forget to use https for the protocol.

We generated the keystore by using the keytool command from the Java Development Kit. But there is a Maven plugin that does the same thing, but we can define all arguments for keytool in our POM. When we run mvn keytool:genkey the keystore is generated and with mvn keytool:clean we can remove the keystore again. If we want to attach the creation of the keystore to the Maven generate-resources phase we must first make sure we invoke keytool:clean otherwise we get an error from keytool that the specified alias already exists. So we can add the following to our POM:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>keytool-maven-plugin</artifactId>
    <executions>
        <execution>
            <phase>generate-resources</phase>
            <id>clean</id>
            <goals>
                <goal>clean</goal>
            </goals>
        </execution>
        <execution>
            <phase>generate-resources</phase>
            <id>genkey</id>
            <goals>
                <goal>genkey</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <keystore>${project.build.directory}/jetty-ssl.keystore</keystore>
        <dname>cn=www.mrhaki.com</dname>
        <keypass>jetty6</keypass>
        <storepass>jetty6</storepass>
        <alias>jetty6</alias>
        <keyalg>RSA</keyalg>
    </configuration>
</plugin>

Now we can invoke mvn jetty:run and the keystore is automatically generated and used by the Jetty plugin.

May 6, 2009

Skipping tests from Maven build in NetBeans

In NetBeans we can disable unit testing in a Maven project. I've been working on a project lately with a lot of different modules. Some of these modules contained failing unit test, but there was nothing I could do about it. In order to keep building the modules we can disable the unit testing.

We can define a custom goal for our Maven project. Therefore we must right-click on the project and select Custom | Goals.... NetBeans opens a dialog window. Here we select the checkbox Skip Tests to disable unit testing. We can save this goal with the name build (skip tests).

Now we have the custom goal available if we right-click on the project and go to Custom.

Another way to disable testing is go to Project Properties | Actions. We get the list of actions NetBeans uses and the Maven goals belonging to the actions. We select the Build project action and check the Skip tests checkbox. Now NetBeans will disable unit testing when we invoke the Build project command.

May 1, 2009

Use anchors in Maven APT project site documentation

Maven supports the APT (Almost Plain Text) format to create documentation. We can even create anchors and links to the anchors in our documentation:

{Simple title}

    We can write our documentation paragraph as always with <<bold>>
    or <italic> text.

{Another section}

    We can link to our {{{Simple_title}Simple title}} section by using the anchor 
    link. We must make sure we replace spaces in an anchor text with underscores.

This will result in the following HTML:

<div class="section"><h2><a name="Simple_title">Simple title</a></h2> 
<p>We can write our documentation paragraph as always with <b>bold</b> or <i>italic</i> text.</p> 
</div> 
<div class="section"><h2><a name="Another_section">Another section</a></h2> 
<p>We can to our <a href="#Simple_title">Simple title</a> section by using the anchor link.</p> 
</div>

April 2, 2009

Change character encoding scheme for Maven resource and compile plugin in NetBeans

In a previous post we have learned how to change the source version for the Maven compiler plugin. But we can also change the character encoding scheme from the same Project Properties dialog window. The Encoding select box let's us change the encoding for the Maven project.

NetBeans add an encoding element to the compiler plugin definition and adds a configuration for the resource plugin in the pom.xml.

<build>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.0.2</version>
            <configuration>
                <source>1.6</source>
                <target>1.6</target>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
        <plugin>
            <artifactId>maven-resources-plugin</artifactId>
            <version>2.2</version>
            <configuration>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
    </plugins>
</build>

April 1, 2009

Change source version for Maven compiler plugin in NetBeans

The Maven compiler plugin allows us to change the source version of the Java files for compilation. If we want to use all the latest support, like assertions, we must set the version to at least 1.4. If we want to use annotations we must set the value to at least 1.5.

In NetBeans we can change this in the Project Properties window. We right-click on our Maven project and select Project Properties. In the Sources tab we can change the Source/Binary Format to the version we want.

NetBeans add the compiler plugin to the pom.xml with the selected version:

<build>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.0.2</version>
            <configuration>
                <source>1.6</source>
                <target>1.6</target>
            </configuration>
        </plugin>
    </plugins>
</build>