January 23, 2017

Elliptic Curve Keys with OpenSSL

List pre-defined curves in OpenSSL:

$ openssl ecparam -list_curves | grep prime256v1
  prime256v1: X9.62/SECG curve over a 256 bit prime field

Generate private key:

$ openssl ecparam -name prime256v1 -genkey -noout -out prime256v1.key.pem

Generate public key:

$ openssl ec -in prime256v1.key.pem -pubout -out prime256v1.pem

Reference: https://wiki.openssl.org/index.php/Command_Line_Elliptic_Curve_Operations

January 12, 2017

Jackson Standard JSON library for Java

Introduction

Jackson is build up by the following modules:

  • Streaming: "jackson-core"
  • Annotations: "jackson-annotations"
  • Databind: "jackson-databind" implements data-binding (and object serialization)

Maven

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.8.5</version>
</dependency>
$ mvn dependency:tree
...
[INFO] +- com.fasterxml.jackson.core:jackson-databind:jar:2.8.5:compile
[INFO] |  +- com.fasterxml.jackson.core:jackson-annotations:jar:2.8.0:compile
[INFO] |  \- com.fasterxml.jackson.core:jackson-core:jar:2.8.5:compile
...

Jackson JSON Parser and Generator

To do JSON parsing and generation you only need one class com.fasterxml.jackson.databind.ObjectMapper.

To parse a json file.

{
    "name": "volvo",
    "weight": 350,
    "active": true,
    "lastUsed": "2017-01-17",
    "properties": {
        "name A": "gold",
        "name B": "silver"
    }
}
public class Vehicle {
    private String name;
    private int weight;
    private boolean active;
    private Date lastUsed;
    private Map<String, String> properties = new HashMap<>();

    public Vehicle() {
    }

    public Vehicle(String name, int weight, boolean active, Date lastUsed, Map<String, String> properties) {
        this.name = name;
        this.weight = weight;
        this.active = active;
        this.lastUsed = lastUsed;
        this.properties = properties;
    }

    @Override
    public String toString() {
        return "Vehicle [name=" + name + ", weight=" + weight + ", active=" + active + ", lastUsed=" + lastUsed
                + ", properties=" + properties + "]";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }

    public Date getLastUsed() {
        return lastUsed;
    }

    public void setLastUsed(Date lastUsed) {
        this.lastUsed = lastUsed;
    }

    public Map<String, String> getProperties() {
        return properties;
    }

    public void setProperties(Map<String, String> properties) {
        this.properties = properties;
    }
}
ObjectMapper mapper = new ObjectMapper();

String file = JacksonTest.class.getClassLoader().getResource("Vehicle.json").getFile();
Vehicle vehicle = mapper.readValue(new File(file), Vehicle.class);
System.out.println(vehicle);

And to generate json.

Vehicle newVehicle = new Vehicle("saab", 200, true, new Date(), new HashMap<String, String>() {
    {
        put("Code A", "foo");
        put("Code B", "bar");
        put("Code C", "code");
    }
});
String json = mapper.writeValueAsString(newVehicle);
System.out.println(json);

Different ObjectMapper#readValue(...) and ObjectMapper#writeValue(...)

Different parse methods:

  • com.fasterxml.jackson.databind.ObjectMapper.readValue(File, Class<T>)
  • com.fasterxml.jackson.databind.ObjectMapper.readValue(URL, Class<T>)
  • com.fasterxml.jackson.databind.ObjectMapper.readValue(String, Class<T>)
  • com.fasterxml.jackson.databind.ObjectMapper.readValue(Reader, Class<T>)
  • com.fasterxml.jackson.databind.ObjectMapper.readValue(InputStream, Class<T>)
  • com.fasterxml.jackson.databind.ObjectMapper.readValue(byte[], Class<T>)
  • com.fasterxml.jackson.databind.ObjectMapper.readValue(DataInput, Class<T>)

Different generation methods:

  • com.fasterxml.jackson.databind.ObjectMapper.writeValue(File, Object)
  • com.fasterxml.jackson.databind.ObjectMapper.writeValue(OutputStream, Object)
  • com.fasterxml.jackson.databind.ObjectMapper.writeValue(DataOutput, Object)
  • com.fasterxml.jackson.databind.ObjectMapper.writeValue(Writer, Object)
  • com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(Object)
  • com.fasterxml.jackson.databind.ObjectMapper.writeValueAsBytes(Object)

Configure ObjectMapper

mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
mapper.setSerializationInclusion(Include.NON_EMPTY);

January 6, 2017

BDD Testing with Cucumber, Java and JUnit

Introduction

BDD (Behaviour-Driven Development) is about designing code by defining your application in terms of behavior.

You start by gathering Tester, Developer and Product Owner (The Three Amigos) and define User Stories. These User Stories are given a Feature name and each Feature is broken down into a Scenario with Steps defines with the keywords: Given, When and Then

Cucumber

Now in Cucumber you write your User Stories (=Feature) in a language called Gherkin. Example:


Feature: Cash withdrawal 

Scenario: Withdrawal from an account in credit 
    Given I have deposited $100.00 in my account 
    When I withdraw $20 
    Then $20 should be dispensed 

Maven Dependency

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

<dependency>
    <groupId>info.cukes</groupId>
    <artifactId>cucumber-java</artifactId>
    <version>${cucumber.version}</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>info.cukes</groupId>
    <artifactId>cucumber-junit</artifactId>
    <version>${cucumber.version}</version>
    <scope>test</scope>
</dependency>

Cucumber Code Structure

All code (production code, test code and gherkin code) must be placed in the same catalog structure.

  • Production Code - src/main/java/se/magnuskkarlsson/examples/cucumber/Account.java
  • Test Code - src/test/java/se/magnuskkarlsson/examples/cucumber/AccountTest.java
  • Test Code - src/test/java/se/magnuskkarlsson/examples/cucumber/AccountSteps.java
  • Gherkin Code - src/test/resources/se/magnuskkarlsson/examples/cucumber/cash_withdrawal.feature

Cucumber Test Code

We need two classes: one JUnit wrapper class and one Cucumber class that implements the steps in your Gherkin Code.

package se.magnuskkarlsson.examples.cucumber;

import org.junit.runner.RunWith;

import cucumber.api.junit.Cucumber;

@RunWith(Cucumber.class)
public class AccountTest {

}
package se.magnuskkarlsson.examples.cucumber;

import static org.hamcrest.CoreMatchers.is;

import org.junit.Assert;

import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;

public class AccountSteps {

    Account account = new Account();

    @Given("^I have deposited \\$(\\d+) in my account$")
    public void deposit(int amount) {
        account.deposit(amount);
    }

    @When("^I withdraw \\$(\\d+)$")
    public void withdraw(int amount) {
        account.withdraw(amount);
    }

    @Then("^My balance should be \\$(\\d+)$")
    public void verifyBalance(int balance) {
        Assert.assertThat(balance, is(account.getBalance()));
    }
}

Verify

You can now either run you JUnit test class AccountTest inside your IDE or run complete test from command line as usual in maven: mvn clean install.

January 5, 2017

JDK Logger/Java Logging API

Background

Since JDK 1.4 there is a default Logging API in Java. The entire Logging API is contained in the package java.util.logging.

Reference: https://docs.oracle.com/javase/8/docs/technotes/guides/logging/overview.html

Usage

import java.util.logging.Level;
import java.util.logging.Logger;

public class LoggerTest {

    private static final Logger log = Logger.getLogger(LoggerTest.class.getName());

    public void something() throws Exception {
        if (log.isLoggable(Level.FINEST)) {
            log.finest("Enter something()...");
        }

        log.info("Logging result");

        log.warning("Another logging result");

        log.log(Level.SEVERE, "Nullpoint here", new NullPointerException("divide by zero"));

        if (log.isLoggable(Level.FINEST)) {
            log.finest("Exit something()");
        }
    }
}

Default Logging Configuration

Java comes with a default logging configuration file, that only contains a ConsoleHandler and writes to standard error.

$JAVA_HOME/lib/logging.properties

############################################################
#       Default Logging Configuration File
#
# You can use a different file by specifying a filename
# with the java.util.logging.config.file system property.  
# For example java -Djava.util.logging.config.file=myfile
############################################################

############################################################
#       Global properties
############################################################

# "handlers" specifies a comma separated list of log Handler 
# classes.  These handlers will be installed during VM startup.
# Note that these classes must be on the system classpath.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.
handlers= java.util.logging.ConsoleHandler

# To also add the FileHandler, use the following line instead.
#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler

# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers.  For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= INFO

############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################

# default file output is in user's home directory.
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter

# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

# Example to customize the SimpleFormatter output format 
# to print one-line log message like this:
#     <level>: <log message> [<date/time>]
#
# java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n

############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################

# For example, set the com.xyz.foo logger to only log SEVERE
# messages:
com.xyz.foo.level = SEVERE

Configuration

To change default configuration, create a new configuration file and add a system property java.util.logging.config.file to you java process, which point to yours configuration file. Example:

java ... -Djava.util.logging.config.file=/tmp/logging.properties ...

The available handlers/appenders and their configuration are:

# Default logging.properties $JRE_HOME/lib/logging.properties
# java -Djava.util.logging.config.file=logging.properties
handlers                                   = java.util.logging.FileHandler, java.util.logging.ConsoleHandler

# Global Logging Level.
.level                                     = INFO

# See https://docs.oracle.com/javase/8/docs/api/java/util/logging/FileHandler.html
# Do NOT limit the messages here, it's controlled by global and/or specific classes
java.util.logging.FileHandler.level        = ALL
java.util.logging.FileHandler.formatter    = java.util.logging.SimpleFormatter
java.util.logging.FileHandler.encoding     = UTF-8
java.util.logging.FileHandler.limit        = 50000
java.util.logging.FileHandler.count        = 10
java.util.logging.FileHandler.pattern      = %h/java-%g.log
java.util.logging.FileHandler.append       = true

# See https://docs.oracle.com/javase/8/docs/api/java/util/logging/ConsoleHandler.html
# Do NOT limit the messages here, it's controlled by global and/or specific classes
java.util.logging.ConsoleHandler.level     = ALL
java.util.logging.ConsoleHandler.encoding  = UTF-8

# See https://docs.oracle.com/javase/8/docs/api/java/util/logging/StreamHandler.html
# Do NOT limit the messages here, it's controlled by global and/or specific classes
java.util.logging.StreamHandler.level      = ALL
java.util.logging.StreamHandler.encoding   = UTF-8

# See https://docs.oracle.com/javase/8/docs/api/java/util/logging/SocketHandler.html
# Do NOT limit the messages here, it's controlled by global and/or specific classes
java.util.logging.SocketHandler.level      = ALL
java.util.logging.SocketHandler.encoding   = UTF-8
java.util.logging.SocketHandler.host       =
java.util.logging.SocketHandler.port       =

# See https://docs.oracle.com/javase/8/docs/api/java/util/logging/MemoryHandler.html
# Do NOT limit the messages here, it's controlled by global and/or specific classes
java.util.logging.MemoryHandler.level      = ALL
java.util.logging.MemoryHandler.size       =
java.util.logging.MemoryHandler.push       =
java.util.logging.MemoryHandler.target     =

# Customize the SimpleFormatter output format
# See https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax 
java.util.logging.SimpleFormatter.format   = %1$tF'T'%1$tT.%1$tL%1$tz [%4$s] %2$s - %5$s %6$s%n
#return String.format(format,                                    // java.util.logging.SimpleFormatter.format
#                     dat,                                       // %1$t<conversion>
#                     source,                                    // %2$s    
#                     record.getLoggerName(),                    // %3$s
#                     record.getLevel().getLocalizedLevelName(), // %4$s
#                     message,                                   // %5$s
#                     throwable);                                // %6$s

# Specific logging level for classes or packages
se.magnuskkarlsson.examples.lambda.level = FINEST

Log Levels

Java Logging API has the following levels: OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL

You define a global log level with .level, then if you need specific configuration you add: <package>[.class].level=<level>

NOTE: ConsoleHandler is special, for that you need to set ConsoleHandler.level for others you don't!

Log Formatter

There exists only two formatter and you typically want to use the SimpleFormatter.

NOTE: Most handlers uses the SimpleFormatter as default, but some do not. For them you need to set the formatter configuration.

Configure java.util.logging.SimpleFormatter

The default format is quite odd, so you want to change that.

The configuration is done with java.util.logging.SimpleFormatter.format. And the value is for the java call in java.util.logging.SimpleFormatter.format(LogRecord)

return String.format(format,                                    // java.util.logging.SimpleFormatter.format
                     dat,                                       // %1$t<conversion>
                     source,                                    // %2$s    
                     record.getLoggerName(),                    // %3$s
                     record.getLevel().getLocalizedLevelName(), // %4$s
                     message,                                   // %5$s
                     throwable);                                // %6$s

To understand the java.lang.String.format(String, Object...) syntax, read https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax.

Configuration in Java EE 6

Background

Dependency Injection has come strong in Java EE 6, which is widely influenced from Spring Framework.

You can use the same pattern to handle configuration.

Code

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

import javax.enterprise.inject.spi.InjectionPoint;

import org.apache.log4j.Logger;

@javax.ejb.Startup
@javax.ejb.Singleton
public class Configuration {

    private final Logger log = Logger.getLogger(Configuration.class);

    private final Map<String, String> configuration = new HashMap<String, String>();

    @javax.annotation.PostConstruct
    public void fetchConfiguration() {
        // load configuration from preferred place and add to map
        configuration.put("se.magnuskkarlsson.examples.lambda.FruitBoundary.noFruits", "999");
    }

    @javax.enterprise.inject.Produces
    public String getString(InjectionPoint point) {
        String fieldClass = point.getMember().getDeclaringClass().getName();
        String fieldName = point.getMember().getName();
        log.info(" >> fieldName : " + fieldClass);
        log.info(" >> fieldName Class : " + fieldName);
        String fieldKey = fieldClass + "." + fieldName;
        String fieldValue = configuration.get(fieldKey);
        log.info("Loaded " + fieldKey + "='" + fieldValue + "'");
        return fieldValue;
    }

    @javax.enterprise.inject.Produces
    public int getInteger(InjectionPoint point) {
        String stringValue = getString(point);
        if (stringValue == null) {
            return 0;
        }
        return Integer.parseInt(stringValue);
    }
}

And to use it

import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/fruit")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public class FruitBoundary {

    @Inject
    private int noFruits;

    @GET
    @Path("/{fruitId}")
    public Fruit getById(@PathParam("fruitId") long fruitId) {
        Fruit fruit = new Fruit();
        fruit.setId(fruitId);
        fruit.setName("Apple " + noFruits);
        fruit.setWeight(35);
        return fruit;
    }
}

And finally you need to add and empty WEB-INF/beans.xml to your achieve to make CDI work.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">

</beans>

January 4, 2017

Java 7 - Implement equals() and hashCode() with java.util.Objects

When using JPA you should implement equals() and hashCode() if you

  • Intend to put instances of persistent classes in a Set.
  • Intend to use reattachment of detached instances.

For JDK 7 and above, you can use the new java.util.Objects class to generate the equals and hash code values.

import java.util.Objects;

public class Fruit {

    private Long id;
    private String name;
    private int weight;

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof Fruit)) {
            return false;
        }
        Fruit fruit = (Fruit) obj;
        return Objects.equals(id, fruit.id) && Objects.equals(name, fruit.name) && Objects.equals(weight, fruit.weight);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, weight);
    }

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

}

Test code

Fruit fruit1 = new Fruit();
fruit1.setId(78L);
fruit1.setName("apple");
fruit1.setWeight(35);

Fruit fruit2 = new Fruit();
fruit2.setId(78L);
fruit2.setName("apple");
fruit2.setWeight(35);

assertTrue(fruit1.equals(fruit2));
assertFalse("Not same instance", fruit1 == fruit2);
assertThat(fruit1, is(equalTo(fruit2)));

January 3, 2017

MySQL – Backup and Restore

Backup

# Backup a database
$ mysqldump --user=name --password database > filename.sql

# Backup a database and gzip
$ mysqldump --user=name --password database | gzip > filename.sql.gz

Restore

# Restore a database
$ mysql --default-character-set=latin1 --user=name --password database < filename.sql

# Restore a database from gzip
$ gunzip -c filename.sql.gz | mysql --default-character-set=latin1 --user=name --password database 

Internationalization (i18n) with ResourceBundle and Properties

To externalize local messages, Java have the java.util.ResourceBundle class.

Create a properties file for the default locale. Then create a new properties for each locale with the suffix _<language>_<COUNTRY>, in the same way you initialize java.util.Locale.Locale(String, String).

Example

messages.properties
messages_sv_SE.properties
messages_en_US.properties

To load the localized text, use java.util.ResourceBundle.getString(String).

Example

messages.properties

hello.label=Hello World!
hello.info=Entered number = %d and string = %s

messages_sv_SE.properties

# Använd UTF-8 encoding för icke ASCII tecken, se t.ex. http://www.utf8-chartable.de/ 
# \u00E5    å
# \u00E4    ä
# \u00F6    ö
# \u00C5    Å
# \u00C4    Ä
# \u00D6    Ö
hello.label=Hall\u00E5 v\u00E4rlden!
hello.info=Entered number = %d and string = %s

And the Java code to load them.

ResourceBundle bundle = ResourceBundle.getBundle("messages");
System.out.println("hello.label : " + bundle.getString("hello.label"));
System.out.println("hello.info : " + String.format(bundle.getString("hello.info"), 2, "en"));

Locale.setDefault(new Locale("sv", "SE"));
ResourceBundle bundleSE = ResourceBundle.getBundle("messages");
System.out.println("hello.label : " + bundleSE.getString("hello.label"));
System.out.println("hello.info : " + String.format(bundleSE.getString("hello.info"), 2, "hej"));

Output

hello.label : Hello World!
hello.info : Entered number = 2 and string = en
hello.label : Hallå världen!
hello.info : Entered number = 2 and string = hej

Java 8 - Base64

Java 8 now supports java.util.Base64.

  • Base64.getEncoder()/Base64.getDecoder() - Output is mapped to a set of characters lying in A-Za-z0-9+/.
  • Base64.getUrlEncoder()/Base64.getUrlDecoder() - Output is mapped to set of characters lying in A-Za-z0-9+_. Output is URL and filename safe.
  • Base64.getMimeEncoder()/Base64.getMimeDecoder() - Simple format, but represented in lines of 76 characters and seperated with '\r\n'.

Example

byte[] bytes = new byte[2048];
new Random().nextBytes(bytes);
System.out.println("Simple :\n" + new String(getEncoder().encode(bytes), UTF_8) + "\n");
System.out.println("URL :\n" + new String(getUrlEncoder().encode(bytes), UTF_8) + "\n");
System.out.println("MIME :\n" + new String(getMimeEncoder().encode(bytes), UTF_8) + "\n");

Output

Simple :
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

URL :
KoogbGxCpjqSlWfn78lUtpKmKYAJY6AwfzvB42RGRezOMaegQk-n3-IeIKMFfC_G1MmZWWLIhqQGD3DEiA58yH_XHUuBhLT4XzFoVIwt8Rfqy0vITcNT8AJxacn-QHrmAV9OMQzrVppW8Y9cv3eT4lSjhb3hca-0C3hc0IMcjUcuA6fgTZe8HksCNiL81LSB5no3rPixLcgw-VI7Ni-x-XSkvraxFZUCdbmcKkQhPlhGaOYws9U01G9iF2scXz8lVGek_yrnXnM9IIMUv6oH5XJyx171Z1ZvPmWL3ZyCAgPkohS6rA04RsPGK5M20t_TN-jE6eMATIsFcTkPBIk3iU4f7bqwqIMeWWZ785c7lf-6BmjiaioK4aH9qh4CMzosT6R6-rOh9mMFVFlIdY7mi_Pv4J25VJsh46VNtytb4CW513nPuGjdRwPDuxaGxnl4LfGmscLatBYPEvH4d87FVPFr5bzCwKyBQdcJPSMUPVCIGWufvhY4il6yJ-jDtArrcZ7rQFucL5vih73cy0zT4ncgV-umhhHAfEkUc06JRtJHBqXEEQrknLxRb0Cg6-1HQ5zS27oAKIoKR249PKOx4JWyZmXzMFmr2rm1E-ihtsaFiEgP_vdNgh3yxjUWTse5D5nJ0RhdoK76n4LFjXeqnbAcp2m40GbCvWNZbUhy4nqAVMHdGBwjbQ9h6Jiee4gRhfKTmPREModnUe_Kos2WIkUzXMKRaXCbwMlwu8wENpycfI6SANlvhoynpPjYu8j30JUGN6Pr0kLo2G-z7oG6juLMegLwX0xlEPSTWwKPxRMmnWuzdD_CiBRS_Jt92rT7FatOhhuQS6jLoLX7HHjemGIpTTKuVYFRCCB47xhf7ElK-9oEnKcfqgv-ewGH_0cNMCjtvvzee3A6h9IUAkrfYGh06yPSICGjER321YYNQMhSM-LvkMg0GVUo7n6fz3D7q6Y2FeB2WI-PeoMBwoK8XR9_cTOgyNcLuNLU8Pq-GE8hUtiFLvpLzTB5YpzHYCR7_xjIeuT6v4j1JqNkQrI24DANUHnmtAOkEKyjg4WC1bXwda1iGo2FblIJViZJz2oODjs-Pndf9XpA8UuMz-R4YgRRiXGDe2QGS-Tj7gQYFp5IUll4FmXKckRcr53ffmeq4dnWY2NdDusOu0svDktkRjxzVXA4-DE6lPBO89Cqobo_M1_JhEE5Jv0LhEYkVIEVnk64dxo3MW4eLMPFIx4bFm5B1Wa0mn0ZMINzEKpM3E73W9MzOWCjkcJhfUE7cl3Lzcis6EI8VQfwfF0XJAyaQSFgXhDaFtyr1eNpAeE8w4tU3EZ1hWZNGT1YbLS-SJl3zENs74O3neqf_rw6wLjNaAuczZVV472QSZlDl8VHqof9ObRpgLzVN93CdGbq_VZKkENZvpAyl2f728N_LB7cx4o2aZdfMtGPP6BZfFHKogaWiDh3l8YZB26WanAIgQEXn7QZZ3Q82wjrz4fCT9qPY2BJdsrOUIjib6v3QhQmz_Y7zVH9Vql36fiOy8sf0WsuSov2IR8eJ7QCzUJULrQ1OrSd-XRgl6ve9ChafmGxKwZBycQzd4Ma092uau_YQ9mdL_6pnTEpBmBf0rHjmuUKakWSbfNIWvaY6wjIO7hvyLopiHECLEpbky5TqhGx2NIc7nd9wAaNEBbKaU60GPXU5fHsw5j5gmKOSYEAtHQeTfPkTH8zqrPoY07ZRJpm5C-4K5yN9ALeq6gMi-acESyismazE_wywDa9_EqAaLVhKzBOuqXD99ucrMoRY0qdoKa5xPv7Ox-7ciOP4IkHO_VDkydnGKBLeGcaRRSJfZAdWc7lUjKs9Y_nwXf8WmbRa9N8VFQD_rQ9LVw4qdLXR68Sj8JAaaK-6aJuVSSZJnZHD8_Kq_4WrmrAvs2JKPKBxSu16XaMvogf1KGBs3s-zL80jlBGTDr5JbU-J2xgiOO7F4377hyh4jBs_IRdxc2aUgQIATJH1hfDtL3aLTwjdPI4xi5Zgoxj0hD_noB75kjk7gvOiM3kzxZFAG7OMyIhdu8Cy9qrPCOKtOJBSEW1ZnCWYvqWniGt23Xo3zmUJTo5pF-xHJqRQNxlnJSfnEglSoQlnFHnln8mJr4g47yg89vSfdCprOJbhyf28fM6MdzsNhIgHXeZ2erT-ZMuhsvkBFR-r53M8RjR405nx2tZhFzpdcbjVkOSjyfzkqwaQYpJarzoos5-ouLMAzgTL7w9FrbZIny82EWTrQjqI81ZxKZTCaZBpmwkNby9XHpVGMeQ9yiaX5RI0UBwUltstbRWiWGeLX5EQ1EOY939SwBXCk0IpuywGyfac2O1GRkedWlm2flSxzua3QxeCflVLyCOdsSjpZJEFkVt1DlO8wxvCeQfYf80g6EBxX1xY4dGAwMUb6Sqzd6MwLdP25VEcN2mkfDQsRbzjvts1rICY7Wsu8fcMLPHvlI4HK-kgfG6Q5QwpLr60VQwi5loaNFBKt0rIAZ1y8lc1WCmQc8UZPnLi4zHC_8QIEN4CnVcfWX7OW54Y3HSlqatMbk_yEhGqckn5ISj-kA_fAmHzf7q2q-Zwmi0WvtR_UxkM_1zigzeCGnhoYe5YHVbjcq0PK8s0iIIxxSUTrWZzPqji8I5UHw4NCIU4R0fP__TgB8XleV2bkg3gYknmNBHtY6-rSz6lQ3RUG4GnUlJL7JSaFwb4yDjK0wtk7XF8f6cwMMEKT0xtWvMp6A=

MIME :
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

Java 8 - Streams

What is Stream?

A sequence of elements supporting sequential and parallel aggregate operations.

How to create?

The java.util.Collection interface has two methods:

  • stream()
  • parallelStream() - supports parallel computing.

How to use?

The java.util.stream.Stream interface has several methods, here are the most common used.

filter

List<String> strings = Arrays.asList(new String[] { "foo", "bar", "", "code" });
String[] rtn = strings.stream().filter(s -> !s.isEmpty()).toArray(String[]::new);
assertThat(new String[] { "foo", "bar", "code" }, is(equalTo(rtn)));

map

List<String> strings = Arrays.asList(new String[] { "foo", "bar", "code" });
String[] rtn = strings.stream().map(String::toUpperCase).toArray(String[]::new);
assertThat(new String[] { "FOO", "BAR", "CODE" }, is(equalTo(rtn)));

sorted

List<String> strings = Arrays.asList(new String[] { "foo", "bar", "code" });
String[] rtn = strings.stream().sorted().toArray(String[]::new);
assertThat(new String[] { "bar", "code", "foo" }, is(equalTo(rtn)));

forEach

List<String> strings = Arrays.asList(new String[] { "foo", "bar", "code" });
strings.stream().forEach(s -> System.out.println(" >> " + s));

collect

List<String> strings = Arrays.asList(new String[] { "foo", "bar", "code" });
List<String> rtn = strings.stream().collect(toList());
assertThat(strings, is(equalTo(rtn)));

java.util.stream.Collectors

Collectors are used of collecting result from Stream operations, but there are several useful methods in the java.util.stream.Collectors interface. Here some of them.

toList()/toSet()

List<String> strings = Arrays.asList(new String[] { "foo", "bar", "code" });
List<String> rtn = strings.stream().collect(toList());
Set<String> rtn1 = strings.stream().collect(toSet());

groupingBy

Map<String, List<String>> rtn1 = Stream.of("foo", "bar", "code", "foo", "bar").collect(groupingBy(identity()));
System.out.println("groupingBy : " + rtn1);
// {bar=[bar, bar], code=[code], foo=[foo, foo]}

Map<String, Long> rtn2 = Stream.of("foo", "bar", "code", "foo", "bar").collect(groupingBy(identity(), counting()));
System.out.println("groupingBy : " + rtn2);
// {bar=2, code=1, foo=2}

January 2, 2017

Java 8 - Lambda Expressions

Syntax

parameter -> expression body
  • Optional type declaration − No need to declare the type of a parameter. The compiler can inference the same from the value of the parameter.
  • Optional parenthesis around parameter − No need to declare a single parameter in parenthesis. For multiple parameters, parentheses are required.
  • Optional curly braces − No need to use curly braces in expression body if the body contains a single statement.
  • Optional return keyword − The compiler automatically returns the value if the body has a single expression to return the value. Curly braces are required to indicate that expression returns a value.

Examples

Old inner class

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println(" >> Hello from thread");
    }
}).start();

With lambda expression

new Thread(() -> System.out.println(" >> Hello from lambda thread")).start();

Old inner class

List<String> strings1 = Arrays.asList(new String[] { "foo", "bar" });
Collections.sort(strings1, new Comparator<String>() {

    @Override
    public int compare(String s1, String s2) {
        return s1.compareTo(s2);
    }
});
assertThat(Arrays.asList(new String[] { "bar", "foo" }), is(equalTo(strings1)));

With lambda expression

List<String> strings2 = Arrays.asList(new String[] { "foo", "bar" });
Collections.sort(strings2, (s1, s2) -> s1.compareTo(s2));
assertThat(Arrays.asList(new String[] { "bar", "foo" }), is(equalTo(strings2)));