How to use Maven Shade Plugin

Maven Shade Plugin provides the capability to package the artifact in an uber-jar, including its dependencies and to shade(rename) the packages of some of the dependencies.

This plugin is used to take all the dependencies and extract the content and put them as is and create one big JAR(Uber. This will be good for runnable jar but if you are planning to use this jar as a maven dependency then there may be some conflicts with the client jar. So we need to use the relocations option to relocate the conflicting classes to a different package.

In this post, I will show how to use this plugin with an example.

The below example is for creating the final Uber jar without relocations.

Create Uber Jar without relocations

Consider that we have a web app which exposes a REST web service and running on a grizzly container. Now, let’s use the shade plugin to create the fat jar.

I am not going to give the code examples here as this post explains about the shade plugin only.

Refer the complete code https://github.com/dkbalachandar/greeting-rest

The below plugin configuration has to be added in the maven pom file.

Refer the configuration section where we have to specify various options. They are given below,

  • mainclass – The entry point of the application
  • createSourcesJar – Create the sources jar as well
  • shadedClassifierName – The name of the classifier used in case the shaded artifact is attached
  • shadedArtifactAttached – It defines whether the shaded artifact should be attached as classifier to the original artifact. If its false, then the original jar will be replaced by the shaded jar

    <plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-shade-plugin</artifactId>
	<version>3.1.0</version>
	<executions>
		<execution>
			<phase>package</phase>
			<goals>
				<goal>shade</goal>
			</goals>
			<configuration>
				<createSourcesJar>true</createSourcesJar>
				<shadedClassifierName>shaded</shadedClassifierName>
				<shadedArtifactAttached>true</shadedArtifactAttached>
				<transformers>
					<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
						<mainClass>com.Grizzly</mainClass>
					</transformer>
				</transformers>
				<filters>
					<filter>
						<artifact>*:*</artifact>
						<excludes>
							<exclude>META-INF/*.SF</exclude>
							<exclude>META-INF/*.DSA</exclude>
							<exclude>META-INF/*.RSA</exclude>
						</excludes>
					</filter>
				</filters>                          
			</configuration>
		</execution>
	</executions>
</plugin>

Then execute ‘mvn package’ command and then check the target folder. You will be able to see the shaded jar and it ends with shaded suffix. Refer the below screenshot.

shaded-jar

When you extract out the contents of the shaded jar, you will see below.

shaded-jar-extraction

Create Uber Jar with relocations

Now we are going to see how we can relocate some of the classes inside the Uber Jar. Lets use the same example and add the below relocations inside the configuration element.


  <configuration>
	<createSourcesJar>true</createSourcesJar>
	<shadedClassifierName>shaded</shadedClassifierName>
	<shadedArtifactAttached>true</shadedArtifactAttached>
	<transformers>
		<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
			<mainClass>com.Grizzly</mainClass>
		</transformer>
	</transformers>
	<filters>
		<filter>
			<artifact>*:*</artifact>
			<excludes>
				<exclude>META-INF/*.SF</exclude>
				<exclude>META-INF/*.DSA</exclude>
				<exclude>META-INF/*.RSA</exclude>
			</excludes>
		</filter>
	</filters>
	<relocations>
		<relocation>
			<pattern>org.glassfish.grizzly.utils</pattern>
			<shadedPattern>org.shaded.glassfish.grizzly.utils</shadedPattern>
			<excludes>
				<exclude>org.glassfish.grizzly.utils.conditions.*</exclude>
			</excludes>
		</relocation>
	</relocations>
</configuration>

Check the element and the child elements. Here, I am planning to rename/move the org.glassfish.grizzly.utils package to org.shaded.glassfish.grizzly.utils and exclude the package org.glassfish.grizzly.utils.conditions.

Then run the mvn package again to create the shaded jar and extract out the contents of the shaded jar. It will look like below.

shaded-jar-relocations

As you can see that the package org.glassfish.grizzly.utils has been moved to org.shaded.glassfish.grizzly.utils but the inner package org.glassfish.grizzly.utils.conditions has been kept it as in the oringal location.

While relocating the packages, this plugin will also update the affected bytecode. So you will not see any issues while running the Uber jar.

Advertisements

Exclude fields from Serialization with GSON API

GSON is a Java serialization/deserialization library to convert Java Objects into JSON and back.

In this post, we are going to see how we can exclude some fields from serialization with the GSON API.

Refer the below model class.

Employee.java



import org.apache.commons.lang3.builder.ToStringBuilder;

public class Employee {

    private String firstName;
    private String lastName;
    private int salary;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }
}

The above Employee class has three fields such as first name, last name and salary. Consider that we want to exclude the salary field while serializing the employee object.

To do so, then we have to create a class which implements the ExclusionStrategy interface and provide our logic over there.

SalaryExclusionStrategy.java


import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;

public class SalaryExclusionStrategy implements ExclusionStrategy {

    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        return "salary".equalsIgnoreCase(f.getName());
    }

    @Override
    public boolean shouldSkipClass(Class clazz) {
        return false;
    }
}


We need to instruct the GSON parser to ignore the salary field. So while creating the GSON object, we have to pass an instance of SalaryExclusionStrategy which contains the logic to ignore the salary field to the addSerializationExclusionStrategy method.

Refer the below code to know how we can do that.


import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class GsonMain {

    private static final Gson GSON = new GsonBuilder()
            .setFieldNamingStrategy(FieldNamingPolicy.IDENTITY)
            .setPrettyPrinting()
            .addSerializationExclusionStrategy(new SalaryExclusionStrategy())
            .create();

    public static void main(String[] args) {
        Employee employee = new Employee();
        employee.setFirstName("Bala");
        employee.setLastName("Samy");
        System.out.println(GSON.toJson(employee));
    }
}


Refer below the output.


{
  "firstName": "Bala",
  "lastName": "Samy"
}

Turn off doclint in JDK 8

Java 8 introduced DocLint which is a quite strict linter for javadoc. By default, the build will be failed if there are any errors while generating the javadocs.

We can disable this check. Refer below to know how to do that.

If you are using the older version of maven-javadoc-plugin (2.10.2 or older). then add the “-Xdoclint:none” inside of the configuration element. Refer below.


<plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-javadoc-plugin</artifactId>
      <version>2.10.2</version>
      <configuration>
         <additionalparam>-Xdoclint:none</additionalparam>
      </configuration>
  </plugin>   

If you are using the latest version of maven-javadoc-plugin(3.0.0), then add “none” inside of the configuration element. Refer below.


<plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-javadoc-plugin</artifactId>
      <version>3.0.0</version>
      <configuration>
         <doclint>none</doclint>
      </configuration>
  </plugin>

If you would not like to add the entire plugin details, Then you can add these configurations in the global maven properties section. Like below.


<properties>
   <additionalparam>-Xdoclint:none</additionalparam>
</properties>

or

<properties>
   <doclint>none</doclint>
</properties>


How to get Git information in Java Application

In this post, we are going to see how to get the GIT information in a Java application.

To do that, we have to use the maven-git-commit-id-plugin plugin. Refer it @maven-git-commit-id-plugin

Now let’s see how we gonna do that.

1. Include the below plugin configuration in your Maven POM file.


  <plugin>
                <groupId>pl.project13.maven</groupId>
                <artifactId>git-commit-id-plugin</artifactId>
                <version>2.1.13</version>
                <executions>
                    <execution>
                        <id>populate-git-commit-information</id>
                        <goals>
                            <goal>revision</goal>
                        </goals>
                        <configuration>
                            <verbose>true</verbose>
                            <dateFormat>MM/dd/yyyy HH:mm:ss Z</dateFormat>
                            <abbrevLength>8</abbrevLength>
                            <generateGitPropertiesFile>true</generateGitPropertiesFile>
                            <generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename>
                        </configuration>
                    </execution>
                </executions>
                <configuration>
                    <dotGitDirectory>${project.basedir}/.git</dotGitDirectory>
                </configuration>
            </plugin> 
            

2. Then do maven install (mvn install). You can set the verbose as false if you don’t want to see the git commit id plugin log.

3. After the build is done, then go to target/classes folder. You will see git.properties contains the git related information. You can read this properties file in your application and use it.

Sample code is given below.


 static Properties properties = new Properties();
    static{
        try (InputStream is = this.class.getClassLoader()
                .getResourceAsStream("git.properties")) {
            properties.load(is);
        }
        catch (IOException io) {
            io.printStackTrace();
        }
    }

File Name: git.properties
File Path: /target/classes/git.properties
Sample properties are given below.



#Generated by Git-Commit-Id-Plugin
#Fri Nov 17 09:49:44 EST 2017
git.tags=
git.commit.id.abbrev=8ef4712a
git.commit.user.email=dkbalachandar@gmail.com
git.commit.message.full=Update Dockerfile
git.commit.id=8ef4712acd077d89b875c5bf67c20af1dbb8bb24
git.commit.id.describe-short=8ef4712-dirty
git.commit.message.short=Update Dockerfile
git.commit.user.name=Balachandar
git.build.user.name=Balachandar
git.commit.id.describe=8ef4712-dirty
git.build.user.email=dkbalachandar@gmail.com
git.branch=master
git.commit.time=09/19/2017 18\:25\:16 -0400
git.dirty=true
git.build.time=11/17/2017 09\:49\:44 -0500
git.remote.origin.url=https\://github.com/dkbalachandar/java-rest-docker.git


Refer my code @java-rest-docker

Curl Commands – Example

Curl command to fetch the response and HTTP status code


Command: 
curl -s -w '%{http_code}' -X GET ${SERVICE_URL}

s flag denotes silent output and w flag denotes write out the http_code in the output data.

Example:
curl -s -w '%{http_code}' -X GET 'https://openlibrary.org/api/books?bibkeys=ISBN:0201558025,LCCN:93005405&format=json'

Sample output:

curl -s -w '%{http_code}' -X GET 'https://openlibrary.org/api/books?bibkeys=ISBN:0201558025,LCCN:93005405&format=json'
{"LCCN:93005405": {"bib_key": "LCCN:93005405", "preview": "noview", "thumbnail_url": "https://covers.openlibrary.org/b/id/240726-S.jpg", "preview_url": "https://openlibrary.org/books/OL1397864M/Zen_speaks", "info_url": "https://openlibrary.org/books/OL1397864M/Zen_speaks"}, "ISBN:0201558025": {"bib_key": "ISBN:0201558025", "preview": "restricted", "thumbnail_url": "https://covers.openlibrary.org/b/id/135182-S.jpg", "preview_url": "https://archive.org/details/concretemathemat00grah_444", "info_url": "https://openlibrary.org/books/OL1429049M/Concrete_mathematics"}}200


To fetch the HTTP header alone.


curl -I -X GET 'https://openlibrary.org/api/books?bibkeys=ISBN:0201558025,LCCN:93005405&format=json'

To print the CURL version information along with the response.


curl -v -X GET 'https://openlibrary.org/api/books?bibkeys=ISBN:0201558025,LCCN:93005405&format=json'

To extract out the HTTP status code


curl -I -s -X GET 'https://openlibrary.org/api/books?bibkeys=ISBN:0201558025,LCCN:93005405&format=json' | grep 'HTTP/1.1' | awk '{print $2}'

How to use HK2 injection in a Standalone Java Application

In this post, we are going to see how we can use the HK2 injection framework in a standalone Java application.

We need to include the below hk2 dependencies in our pom file. Here is the pom file.


 <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>
    <name>Hk2 Java standlaone application</name>
    <version>1.0.0</version>
    <artifactId>hk2-java-sample</artifactId>
    <groupId>com</groupId>
    <properties>
        <commons-lang3-v>3.6</commons-lang3-v>
        <maven-compiler-plugin-v>3.7.0</maven-compiler-plugin-v>
        <hk2-v>2.5.0-b36</hk2-v>
        <junit-v>4.12</junit-v>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>${commons-lang3-v}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit-v}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.hk2</groupId>
            <artifactId>hk2</artifactId>
            <version>${hk2-v}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.hk2</groupId>
            <artifactId>hk2-junitrunner</artifactId>
            <version>${hk2-v}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler-plugin-v}</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.glassfish.hk2</groupId>
                <artifactId>hk2-inhabitant-generator</artifactId>
                <version>${hk2-v}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>generate-inhabitants</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Consider that, we have a standalone application which will be used for adding, deleting, updating the employee details and the back end is a HashMap.

We have two interfaces(EmployeeService and EmployeeDao) and its implemetation classes(EmployeeServiceImpl and EmployeeDaoImpl) in this application.

To make use of HK2 injection, All our interfaces should be annotated with @Contract and all the implementation classes should be annotated with @Service.

Refer the below code to know how to do that.


package com.service;

import com.model.Employee;
import org.jvnet.hk2.annotations.Contract;

@Contract
public interface EmployeeService {
    public Employee fetch(String id);
    public Employee add(Employee employee);
    public void delete(String id);
    public Employee update(Employee employee);
}



package com.service.impl;

import com.dao.EmployeeDao;
import com.model.Employee;
import com.service.EmployeeService;
import org.jvnet.hk2.annotations.Service;

import javax.inject.Inject;
import java.util.UUID;

@Service
public class EmployeeServiceImpl implements EmployeeService {

    private EmployeeDao employeeDao;

    @Inject
    public EmployeeServiceImpl(EmployeeDao employeeDao){
        this.employeeDao = employeeDao;
    }

    @Override
    public Employee fetch(String id) {
        return employeeDao.fetch(id);
    }

    @Override
    public Employee add(Employee employee) {
        employee.setId(UUID.randomUUID().toString());
        return employeeDao.add(employee);

    }

    @Override
    public void delete(String id) {
        employeeDao.delete(id);
    }

    @Override
    public Employee update(Employee employee) {
        return employeeDao.update(employee);
    }
}



package com.dao;

import com.model.Employee;
import org.jvnet.hk2.annotations.Contract;

@Contract
public interface EmployeeDao {

    public Employee fetch(String id);
    public Employee add(Employee employee);
    public void delete(String id);
    public Employee update(Employee employee);
}


package com.dao.impl;

import com.dao.EmployeeDao;
import com.model.Employee;
import org.jvnet.hk2.annotations.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class EmployeeDaoImpl implements EmployeeDao {

    private static Map empLocalCache = new HashMap();

    public Employee fetch(String id) {
        return empLocalCache.get(id);
    }

    public Employee add(Employee employee) {
        empLocalCache.put(employee.getId(), employee);
        return empLocalCache.get(employee.getId());
    }

    public void delete(String id) {
        empLocalCache.remove(id);
    }

    public Employee update(Employee employee) {
        empLocalCache.put(employee.getId(), employee);
        return empLocalCache.get(employee.getId());
    }
}


Below is my main class which will use the Employee service class to perform add, update and delete operations with Employee Object.


package com;

import com.model.Employee;
import com.service.EmployeeService;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.utilities.ServiceLocatorUtilities;

public class Application {

    public static void main(String[] args) {

        ServiceLocator serviceLocator = ServiceLocatorUtilities.createAndPopulateServiceLocator();
        EmployeeService employeeService = serviceLocator.getService(EmployeeService.class);
        Employee employee = new Employee();
        employee.setFirstName("First Name");
        employee.setLastName("Last Name");

        System.out.println("Add employee details");
        Employee employeeAddResponse = employeeService.add(employee);
        System.out.println("The employee details after it has been added Employee: "+employeeAddResponse);

        System.out.println("Now fetch the employee details with ID");
        Employee employeeFetchResponse = employeeService.fetch(employee.getId());
        System.out.println("Employee :"+employeeFetchResponse);

        System.out.println("Now update the employee details");
        employee.setFirstName("Bala");
        employee.setLastName("Samy");
        Employee employeeUpdateResponse = employeeService.update(employee);
        System.out.println("Employee After Updation:"+employeeUpdateResponse);

        System.out.println("Now delete the employee details");
        employeeService.delete(employee.getId());
        System.out.println("Employee After Deletion:"+ employeeService.fetch(employee.getId()));

    }
}


To initialize all the HK2 dependencies, we have to call the ServiceLocatorUtilities.createAndPopulateServiceLocator() which will perform the initialization and return the ServiceLocator object.

We can make use of ServiceLocator object for getting the Object instance like below

EmployeeService employeeService = serviceLocator.getService(EmployeeService.class);

Before executing this, we need to do an important step which is to create the hk2 default file under our target/classes/META-INF folder. The hk2-inhabitant-generator plugin will help us to generate that file. Hence we need to include this plugin in our file. So we have to do maven install/package before we start executing our junit test cases.

The sample default file content will be like this,

#
# Generated on Thu Nov 09 17:04:57 EST 2017 by hk2-inhabitant-generator
#

[com.dao.impl.EmployeeDaoImpl]S
contract={com.dao.EmployeeDao}

[com.service.impl.EmployeeServiceImpl]S
contract={com.service.EmployeeService}

The output of my program is given below,



Add employee details
The employee details after it has been added Employee: Employee{id='f25b119a-820e-4162-af99-5c3580fe06f1', firstName='First Name', lastName='Last Name}
Now fetch the employee details with ID
Employee :Employee{id='f25b119a-820e-4162-af99-5c3580fe06f1', firstName='First Name', lastName='Last Name}
Now update the employee details
Employee After Updation:Employee{id='f25b119a-820e-4162-af99-5c3580fe06f1', firstName='Bala', lastName='Samy}
Now delete the employee details
Employee After Deletion:null


 

Refer the code @hk2-java-sample

 

 

Apache Commons CSV

Apache Commons CSV library is used for creating and reading CSV files.

In this post, we are going to see, how we can use this library with an example.

First thing is to include the below dependency in your maven pom file.


 <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-csv</artifactId>
            <version>1.5</version>
  </dependency>

Consider that the employee information is available in a CSV file called (employee.csv). The sample data are given below.

Id First Name Last Name Age
1 John Arthur 25
2 Sam Andrew 26
3 Hary Peter 26

Let’s see how we can read this CSV file in a Java Program with Commons CSV.



import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVRecord;

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;

public class ReadCSVFileMain {

    public static void main(String[] args) throws IOException {

        String filePath = "/tmp/employee.csv";
        List employeeList = new ArrayList();
        Reader reader = null;
        try {
            String[] headers = { "Id", "First Name", "Last Name", "Age" };
            reader = new FileReader(filePath);
            Iterable records = CSVFormat.DEFAULT.withHeader(headers).withFirstRecordAsHeader().parse(reader);
            records.forEach(csvRecord -> {
                Employee employee = new Employee();
                employee.setId(Integer.valueOf(csvRecord.get("Id")));
                employee.setFirstName(csvRecord.get("First Name"));
                employee.setLastName(csvRecord.get("Last Name"));
                employee.setAge(Integer.valueOf(csvRecord.get("Age")));
                employeeList.add(employee);
            });
        }

        finally {
            if (reader != null) {
                reader.close();
            }
        }
        System.out.println("Parsed Content: employeeList::" + employeeList);
    }
}



public class Employee {

    private Integer id;
    private String firstName;
    private String lastName;
    private Integer age;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Employee{");
        sb.append("id=").append(id);
        sb.append(", firstName='").append(firstName).append('\'');
        sb.append(", lastName='").append(lastName).append('\'');
        sb.append(", age=").append(age);
        sb.append('}');
        return sb.toString();
    }

}


The above program is used to parse the employee.CSV file and maps the contents into Employee object. I have created a string array which holds the header names and using that while parsing the file.

The output will be like below,


Parsed Content: employeeList::[Employee{id=1, firstName='John', lastName='Arthur', age=25}, Employee{id=2, firstName='Sam ', lastName='Andrew', age=26}, Employee{id=3, firstName='Hary', lastName=' Peter', age=26}]

Now lets see how we can create a CSV file with an example.



import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class CreateCSVFileMain {

    public static void main(String[] args) throws IOException {

        String filePath = "/tmp/employee_new.csv";
        String[] headers = { "Id", "First_Name", "Last_Name", "Age" };
        List employeeList = new ArrayList();
        Employee employee1 = new Employee();
        employee1.setId(1234);
        employee1.setFirstName("fname1");
        employee1.setLastName("lname1");
        employee1.setAge(45);
        Employee employee2 = new Employee();
        employee2.setId(1234);
        employee2.setFirstName("fname1");
        employee2.setLastName("lname1");
        employee2.setAge(45);
        employeeList.add(employee1);
        employeeList.add(employee2);

        FileWriter writer = null;
        try {
            writer = new FileWriter(filePath);
            try (CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT.withHeader(headers))) {
                employeeList.forEach(employee -> {
                    try {
                        csvPrinter.printRecord(employee.getId(), employee.getFirstName(), employee.getLastName(),
                                               employee.getAge());
                    }
                    catch (IOException io) {
                        io.printStackTrace();
                    }
                });
            }
        }
        finally {
            if (writer != null) {
                writer.close();
            }
        }
    }
}


If we don’t want the header then, don’t specify with header.
The content of the created employee_new.csv file is given below,


Id,First_Name,Last_Name,Age
1234,fname1,lname1,45
1234,fname1,lname1,45