You are on page 1of 12

 

Introduction
This book is about how to implements Java applications that can be run from the command-line.

Command Line tools comes in handy especially when we need to automate tedious tasks.

Being myself a developer I chose to draw a different path while writing this book. Rather than generic
descriptions on each framework, we will implement a real application: Sniper .

Sniper is a Java CLI tool useful to perform every kind of HTTP tasks. It is like cURL but with the ability to send
documents with parameterized fields. We will provide a template with placeholders (any kind of text file
XML, HTML, JSON, CSV etc.) and a model (a JSON file were we can define the template placeholders related
values).

Purposes and areas of use of Sniper:

to test a remote server REST api (I developed Sniper in order to test Elasticsearch custom queries)
to test SOAP api - with a static envelope or a dynamic, creating a template file for the envelope and the
model for the fields.
to automate web forms data uploads
to automate remote files download
add your own idea and share it!

But, above all, Sniper is just a fun way to introduce yourself to Java CLI applications.

What Topics Are Waiting for You?


Let me describe in short the covered topics before we move on and dig deeper into Java CLI Tools and
Sniper obviously.

Covered Topics
I will show you only frameworks that can be used for both Desktop, Android and Server Side applications.

Our project will use the following frameworks:

Apache Commons CLI for parsing command line options passed to ours Java applications.
OkHttp an HTTP & HTTP/2 client designed with fluent builders and immutability. It supports both
synchronous blocking calls and asynchronous calls with callbacks.
Chunk Templates for Java a Template Engine for Java, similar to Apache Velocity, FreeMarker or Jtwig
Moshi a JSON library for Android and Java that makes it easy to parse JSON into Java objects

Who Is This Book For?


This book is for any Java developer, Software Designer and Project Manager.

Reading this book you will take away new ideas on how to optimize your command-line application, how to
organize the options, how to interacting with REST API’s or webservices using OkHttp.

You’ll recognize goodies that are applicable for the projects you’re working on.

Following the recipes in this book you will have the opportunity to see some of the most useful Java
frameworks and understand the reason for these choices and why prefer one to the other.

The Source Code

Command Line Arguments Parsing


Apache Commons CLI Overview
The Apache Commons CLI library provides an API for parsing command line options passed to ours java
programs. Commons CLI supports different types of options:

POSIX like options (ie. tar -zxvf foo.tar.gz )


GNU like long options (ie. du --human-readable --max-depth=1 )
Java like properties (ie. java -Djava.awt.headless=true -Djava.net.useSystemProxies=true Foo )
Short options with value attached (ie. gcc -O2 foo.c )
long options with single hyphen (ie. ant -projecthelp )

It's also able to print help messages detailing the options available for a command line tool.

A typical help message displayed by Commons CLI looks like this:

 usage: java ‐jar sniper.jar [options] <url>
 ‐d,‐‐data <data>               Sends the specified data location a POST
                                request
 ‐F,‐‐form <name=content>       Emulate a filled‐location form
 ‐H,‐‐header <header>           Extra header to include location the
                                request
 ‐h,‐‐help                      Usage help
 ‐m,‐‐model <file>              Json data model for the specified template
 ‐O,‐‐remote‐name               Write output to a local file named like
                                the remote file
 ‐o,‐‐output <file>             Write output to <file> instead of stdout
 ‐v,‐‐verbose                   Useful for debugging
 ‐X,‐‐request <method>          Specifies a custom request method

The Apache Commons CLI documentation's introduces the three stages of command line processing:
"definition", "parsing", and "interrogation".

Maven Dependency
Add the library as a dependency into the  pom.xml :

 <dependency>
    <groupId>commons‐cli</groupId>
    <artifactId>commons‐cli</artifactId>
    <version>1.4</version>
</dependency>

Options Definition Stage


As the official user guide states, each command line must define the set of options that will be used to
define the interface to the application. This is the first stage: the "Definition Stage".

CLI uses the Options class, as a container for Option instances.

Let's define the options of our great command line tool:

 Options options = new Options();
 
options.addOption(Option.builder("H")
                  .longOpt("header")
                  .hasArg(true)
                  .argName("header")
                  .desc("Extra header to include location the request")
                  .required(false)
                  .build());
 
options.addOption(Option.builder("h")
                  .longOpt("help")
                  .hasArg(false)
                  .desc("Usage help")
                  .required(false)
                  .build());
 
options.addOption(Option.builder("v")
                  .longOpt("verbose")
                  .hasArg(false)
                  .desc("Useful for debugging")
                  .required(false)
                  .build());

Arguments Parsing Stage


Once we have ours command-line options, we can proceed to the next stage, the "Parsing Stage".

The next gist demonstrates how simple is parsing the command-line with Apache Commons CLI:
 CommandLineParser parser = new DefaultParser();
CommandLine commandLine = parser.parse(options, args);
if (commandLine == null || commandLine.hasOption("h")) {
  help(options);
}

Command line Interrogation Stage


The interrogation stage is where the application queries the CommandLine to decide what execution branch
to take depending on boolean options and uses the option values to provide the application data.

The below code listing demonstrates use of CommandLine.hasOption() to determine if an option's


particular flag is present without regard for whether a value is provided for that flag (appropriate for -v / --
verbose in our tool).

Likewise, the code shows that CommandLine.getOptionValues() can be used to obtain the array of values
associated with the the provided command-line flag (appropriate for the -H/ --header` option in our tool).

 final String[] headers = commandLine.getOptionValues('H');
final verbose = commandLine.hasOption('v');

Why Apache Commons CLI?


is open source and licensed with the Apache License, Version 2.0.
does not require any third-party libraries to be downloaded or referenced separately.

has been around for a while; its initial 1.0 release was in November 2002.
supports both long and short syntax for command-line arguments:

POSIX like options (single hyphen)


Java like properties (using -D )
Short options with value attached
Long options with single hyphen and with double hyphen (GNU style)

Executing the Http Requests


 

OkHttp Overview
OkHttp is an efficient HTTP & HTTP/2 client for Android and Java applications.

It comes with advanced features such as:

connection pooling (if HTTP/2 isn’t available)


transparent GZIP compression
response caching
ability to recover from common connection problems
designed for both synchronous and asynchronous calls.

Maven Dependency
Let’s first add the library as a dependency into the  pom.xml :

 <dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>(insert latest version)</version>
</dependency>

To see the latest dependency of this library check out the page on Maven Central

Initializing & Configuring OkHttpClient

 final OkHttpClient client = new OkHttpClient.Builder()
  .connectTimeout(10, TimeUnit.SECONDS)
  .writeTimeout(10, TimeUnit.SECONDS)
  .readTimeout(30, TimeUnit.SECONDS)
  .build();

Logging Interceptor
To log the HTTP request and the response data we'll use HttpLoggingInterceptor.

 HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(Level.BASIC);
 
OkHttpClient client = new OkHttpClient.Builder()
  .addInterceptor(logging)
  .build();

You can change the log level at any time by calling setLevel .

To log to a custom location, pass a Logger instance to the constructor.

 HttpLoggingInterceptor logging = new HttpLoggingInterceptor(new Logger() {
  @Override public void log(String message) {
    System.out.println(message);
  }
});

Maven Dependency
 <dependency>
  <groupId>com.squareup.okhttp3</groupId>
  <artifactId>logging‐interceptor</artifactId>
  <version>(insert latest version)</version>
</dependency>

GET Request

Synchronous
build a Request object
make a Call with this request
get back an instance of Response

 Request request = new Request.Builder().url(API_URL).build();
 
Call call = client.newCall(request);
 
Response response = call.execute();

Asynchronous
build a Request object
enqueue a Call with this request
as soon as response headers are ready a callback will be triggered allowing us to read the response.

 Request request = new Request.Builder().url(API_URL).build();
 
Call call = client.newCall(request);
call.enqueue(new Callback() {
  public void onResponse(Call call, Response response) 
    throws IOException {
      // ...
  }
  
  public void onFailure(Call call, IOException e) {
    fail();
  }
});

Query Parameters
To add some query parameters to our GET request:

use the HttpUrl.Builder class invoking the method addQueryParameter


pass the built Url to our Request instance
 HttpUrl url = new HttpUrl.Builder()
  .scheme(API_SCHEME)
  .host(API_HOST)
  .addQueryParameter("uid", "123")
  .build();
 
Request request = new Request.Builder().url(url).build();

POST Request

Form Data
build a RequestBody of type FormBody
pass this RequestBody to the Request instance

 RequestBody body = new FormBody.Builder()
  .add("username", "johndoe")
  .add("password", "secret!")
  .build();
 
Request request = new Request.Builder()
  .url(API_URL)
  .post(body)
  .build();

JSON

 String json = "{\"id\":123,\"username\":\"JohnDoe\"}";
 
RequestBody body = RequestBody.create(
  MediaType.parse("application/json; charset=utf‐8"), json);
 
Request request = new Request.Builder()
  .url(API_URL)
  .post(body)
  .build();

Multipart
 RequestBody body = new MultipartBody.Builder()
  .setType(MultipartBody.FORM)
  .addFormDataPart("firstname", "John")
  .addFormDataPart("surname", "Doe")
  .addFormDataPart("file", "resume.pdf", 
    RequestBody.create(MediaType.parse("application/pdf"),
      new File("Documents/cv_en.pdf")))
    .build();
 
Request request = new Request.Builder()
  .url(API_URL)
  .post(body)
  .build();

Basic Authentication
Here an example about how to do a GET Request with Basic Authentication credentials.

 Request request = new Request.Builder()
    .url(API_URL)
    .addHeader("Authorization", Credentials.basic("johndoe", "magik"))
    .build();

Creating text templates for HTTP Requests


 

Chunk Template Engine Overview


Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker.

Can be used for Android, Desktop and Server Side Java applications
Nestable loops and conditionals (if-elseIf-else).
Includes and Macros for easy composition.
Flexible null-handling; template designer may specify default tag values.
Support for theme layers and inheritance.

Maven Dependency
Add the library as a dependency into the  pom.xml :

 <dependency>
  <groupId>com.x5dev</groupId>
  <artifactId>chunk‐templates</artifactId>
  <version>(insert latest version)</version>
</dependency>
To see the latest dependency of this library check out the page on Maven Central

Warming Up
First we define a template

 {#example_1}
<div>
  Earth to {$name}.  Come in, {$name}.
</div>
{#}

Next we fill it setting the variables

 Theme theme = new Theme("examples");
     
// Fetch template from this file: themes/examples/hello.chtml
// Inside that file there is a template "snippet" named #example_1
Chunk html = theme.makeChunk("hello#example_1");     
html.set("name", "Bob");
html.render( out );
// or, render to a string
String output = html.toString();

To learn all the available features of this template engine, go to this url
http://www.x5software.com/chunk/examples/ChunkExample

Parsing JSON templates models


Moshi Overview
Moshi is an open source modern JSON parsing library that can be used for both Android, Desktop and
Server Side Java applications.

Intuitive
Built-in support for reading and writing Java’s core data types
Custom Type Adapters it's easy to customize how values are converted to and from JSON
Registering an annotation with a JsonAdapter is awesome!
Nice feauture the sopport of non-strict JSON (setLenient, string ⇔ int conversion).

Maven Dependency
Add the library as a dependency into the  pom.xml :
 <dependency>
  <groupId>com.squareup.moshi</groupId>
  <artifactId>moshi</artifactId>
  <version>(insert latest version)</version>
</dependency>

To see the latest dependency of this library check out the page on Maven Central

Convert JSON to POJO

 String json = "{\"username\":\"johndoe\",\"country\":"USA"}";
 
Moshi moshi = newMoshi.Builder().build();
 
User user = moshi.adapter(User.class).fromJson(json);

where User is a simple and classic POJO

 public class User {
  private String username;
  private String country;
 
  public String getName() { return username; }
  public void setName(final String value) { this.name = value; }
  
  public String getCountry() { return username; }
  public void setCountry(final String value) { this.country = value; }
}

Custom field names with @Json


Use @Json to specify how Java fields map to JSON names. This is necessary when the JSON name contains
spaces or other characters that aren’t permitted in Java field names

 {
  "username": "johndoe",
  "country‐code": "USA"
}

With @Json its corresponding Java class is easy:

 public class User {
  private String username;
  @Json(name = "country‐code")
  private String country;
 
  public String getName() { return username; }
  

  //...
 

Alternate type adapters with @JsonQualifier


Similar to the qualifier annotations in dependency injection tools, use @JsonQualifier to customize how a
type is encoded for some fields without changing its encoding everywhere.

Here is a Json with two integers and an hex value

 {
  "width": 1024,
  "height": 768,
  "color": "#ff0000"
}

Our class uses integers for colors

 class Rectangle {
  int width;
  int height;
  int color;
}

First we define a qualifier annotation

 @Retention(RUNTIME)
@JsonQualifier
public @interface HexColor {
}

Then we apply @HexColor annotation to our class:

 class Rectangle {
  int width;
  int height;
  @HexColor int color;
}

And finally define a type adapter to handle it:


 /** Converts strings like #ff0000 to the corresponding color ints. */
class ColorAdapter {
  @ToJson String toJson(@HexColor int rgb) {
    return String.format("#%06x", rgb);
  }
 
  @FromJson @HexColor int fromJson(String rgb) {
    return Integer.parseInt(rgb.substring(1), 16);
  }
}

You might also like