Extremely Serious

Category: Java 17

Checksum Using SHA-256 MessageDigest

//The data to be checksumed.
final var data = "Hello";
final MessageDigest md;
try {
    md = MessageDigest.getInstance("SHA-256");
    try (DigestInputStream dis = new DigestInputStream(
            //This can be replaced with FileInputStream.
            //The data just needs to be a File path.
            new ByteArrayInputStream(data.getBytes())
            , md)) {
        final var buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = dis.read(buffer)) != -1) {
            // Read the data into the buffer to update the digest
            md.update(buffer, 0, bytesRead);
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    final var digest = md.digest();
    final var sb = new StringBuilder();
    for (final byte b : digest) {
        sb.append(String.format("%02x", b & 0xff));
    }
    System.out.println("Checksum: " + sb.toString());
} catch (NoSuchAlgorithmException e) {
    throw new RuntimeException(e);
}

Retrieving the X509 Certificate Information in Java

//This will hold the target leaf certificate.
Optional<Certificate> cert;

try {
    //Must hold the target URL.
    final var targetUrl = "https://www.google.com";
    final var httpsURL = new URL(targetUrl);
    final var connection = (HttpsURLConnection) httpsURL.openConnection();
    connection.connect();

    //Retrieve the first certificate. This is normally the leaf certificate.
    cert = Arrays.stream(connection.getServerCertificates()).findFirst();
} catch (IOException e) {
    throw new RuntimeException(e);
}

cert.ifPresent(cer -> {
    //Check if the instance of certificate is of type of X509Certificate.
    if(cer instanceof final X509Certificate x509) {
        System.out.println("Subject: " + x509.getSubjectX500Principal().getName());
        System.out.println("From: " + x509.getNotBefore());
        System.out.println("To: " + x509.getNotAfter());
        System.out.println("Duration: " + ChronoUnit.DAYS.between(LocalDate.now(), LocalDate.ofInstant(x509.getNotAfter().toInstant(),ZoneId.systemDefault())));
        System.out.println("\n");
    }
});

Container Aware JVM Parameters for Heap Memory

JVM Parameters

Modify the java heap memory based on container memory using one of the following JVM parameters:

Parameter Description
-XX:InitialRAMPercentage The initial size of the heap based on the total container memory.
-XX:MinRAMPercentage The maximum heap size based on the size of the JVM running on small heap. The small is heap is of approximately 125MB.
-XX:MaxRAMPercentage The maximum heap size based on the size of the JVM running on greater than small heap.

Displaying the Current Value

Use the following command the display the current value of the container aware JVM Parameters using the following:

docker container run -it --rm openjdk:17.0.2-slim java -XX:+PrintFlagsFinal -version | grep -E ".*RAMPercentage"

Expect to see something like the following:

   double InitialRAMPercentage                     = 1.562500
                   {product} {default}
   double MaxRAMPercentage                         = 25.000000
                   {product} {default}
   double MinRAMPercentage                         = 50.000000
                   {product} {default}

Checking the Current Memory and CPU using JShell

  1. Open a jshell in the container using the following:

    docker container run -it --rm openjdk:17.0.2-slim jshell
  2. Paste the following command in jshell:

    var rt = Runtime.getRuntime();
    System.out.printf("Heap size: %dMB%nMaximum size of heap: %dMB%nAvailable processors: %d%n", 
     rt.totalMemory()/1024/1024, rt.maxMemory()/1024/1024, rt.availableProcessors());
  3. Press enter.

    Expect to see something similar to the following:

    rt ==> java.lang.Runtime@1d81eb93
    Heap size: 252MB
    Maximum size of heap: 3966MB
    Available processors: 16
    $2 ==> java.io.PrintStream@34c45dca
  4. Type the following in jshell and press enter.

    /exit

Allocating a Memory and CPU to a Container

  1. Execute the following command to allocate 100mb of memory and 1 CPU to the container:

    docker container run -it --rm -m 100m --cpus=1 openjdk:17.0.2-slim jshell
  2. Paste the following command in jshell:

    var rt = Runtime.getRuntime();
    System.out.printf("Heap size: %dMB%nMaximum size of heap: %dMB%nAvailable processors: %d%n", 
     rt.totalMemory()/1024/1024, rt.maxMemory()/1024/1024, rt.availableProcessors());
  3. Press enter.

    Expect to see something similar to the following:

    rt ==> java.lang.Runtime@6659c656
    Heap size: 7MB
    Maximum size of heap: 48MB
    Available processors: 1
    $2 ==> java.io.PrintStream@2d8e6db6

    Notice the following:

    Value
    Heap size 7MB
    Maximum size of heap 48MB
    Available processors 1
  4. Type the following in jshell and press enter.

    /exit

Modifying the Allocated Maximum Size of Heap Memory

  1. Execute the following command to allocate 100mb of memory and 1 CPU to the container:

    docker container run -it --rm -m 100m --cpus=1 openjdk:17.0.2-slim jshell -R-XX:MinRAMPercentage=80
  2. Paste the following command in jshell:

    var rt = Runtime.getRuntime();
    System.out.printf("Heap size: %dMB%nMaximum size of heap: %dMB%nAvailable processors: %d%n", 
     rt.totalMemory()/1024/1024, rt.maxMemory()/1024/1024, rt.availableProcessors());
  3. Press enter.

    Expect to see something similar to the following:

    rt ==> java.lang.Runtime@6659c656
    Heap size: 7MB
    Maximum size of heap: 77MB
    Available processors: 1
    $2 ==> java.io.PrintStream@2d8e6db6

    Notice the Maximum size of heap become 77MB. This is because of the JVM argument -XX:MinRAMPercentage=80 passed in jshell as:

    -R-XX:MinRAMPercentage=80

    We use the --XX:MinRAMPercentage=80 because the memory allocated is a small heap.

  4. Type the following in jshell and press enter.

    /exit

Generating Random Number

Listing the Available Random Algorithms

System.out.printf("%-13s| %s\n-------------|---------------------\n", "Group", "Name");
java.util.random.RandomGeneratorFactory.all()
        .sorted(java.util.Comparator.comparing(java.util.random.RandomGeneratorFactory::name))
        .forEach(factory -> System.out.printf("%-13s| %s\n", factory.group(), factory.name()));

The output is similar to the following:

Group        | Name
-------------|---------------------
LXM          | L128X1024MixRandom
LXM          | L128X128MixRandom
LXM          | L128X256MixRandom
LXM          | L32X64MixRandom
LXM          | L64X1024MixRandom
LXM          | L64X128MixRandom
LXM          | L64X128StarStarRandom
LXM          | L64X256MixRandom
Legacy       | Random
Legacy       | SecureRandom
Legacy       | SplittableRandom
Xoroshiro    | Xoroshiro128PlusPlus
Xoshiro      | Xoshiro256PlusPlus

Check from the following address on how to choose which algorithm to choose:

https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/random/package-summary.html#algorithms

Creating an instance of RandomGenerator

The default algorithm (i.e. L32X64MixRandom)

var generator = java.util.random.RandomGenerator.getDefault();

The specific algorithm (e.g. L64X128MixRandom)

var generator = java.util.random.RandomGenerator.of("L64X128MixRandom");

Use the static method RandomGenerator.of and pass the name of the algorithm.

Generating Random Number

Use one of the following instance methods of the RandomGenerator class:

  • nextDouble()
  • nextDouble(double bound)
  • nextDouble(double origin, double bound)
  • nextFloat()
  • nextFloat(float bound)
  • nextFloat(float origin, float bound)
  • nextInt()
  • nextInt(int bound)
  • nextInt(int origin, int bound)
  • nextLong()
  • nextLong(long bound)
  • nextLong(long origin, long bound)

The origin parameter is inclusive and bound parameter is exclusive.

Example using a classic random algorithm

 var generator = java.util.random.RandomGenerator.of("Random");
 System.out.println(generator.nextInt());

The output is a random integer number.

Generating Random Number Using Streams

Use one of the following instance methods of RandomGenerators class to create streams:

  • doubles()
  • doubles(double origin, double bound)
  • doubles(long streamSize)
  • doubles(long streamSize, double origin, double bound)
  • ints()
  • ints(int origin, int bound)
  • ints(long streamSize)
  • ints(long streamSize, int origin, int bound)
  • longs()
  • longs(long origin, long bound)
  • longs(long streamSize)
  • longs(long streamSize, long origin, long bound)

The origin parameter is inclusive and bound parameter is exclusive.

Example using a classic random algorithm

var generator = java.util.random.RandomGenerator.of("Random");
var randomInt = generator.ints(100, 1, 100)
        .filter(number -> number % 2 == 0 )
        .findFirst();
randomInt.ifPresent(System.out::println);

The output is a random integer number.

Sealed Classes and Interfaces

Sealed classes and interfaces restrict which other classes or interfaces may extend or implement them.

public sealed interface IAnimal 
    permits AquaticAnimal, LandAnimal {
    String lifespan();
}

Notice the use of the sealed modifier and permits keyword. The sealed modifier indicates that the class or interface is restricted. The permit keyword indicates which classes (or interfaces) can implement (or extends) it. Thus, the sealed modifier and the permit keyword must be used together.

public sealed abstract class AquaticAnimal implements IAnimal permits SeaTurtle {
}

The class (or interface) that can implement (or extend) a sealed interface (or class) must have one of the following modifiers:

  • sealed
  • non-sealed
  • final
public non-sealed abstract class LandAnimal implements IAnimal {
}

Using the non-sealed modifier indicates that this class can be extended without restriction.

public sealed class Dog extends LandAnimal permits Boxer {
    @Override
    public String lifespan() {
        return "10 - 13 years";
    }
}

Limiting the hierarchy of Dog class to just Boxer class.

public final class Boxer extends Dog {
    @Override
    public String lifespan() {
        return "10 - 12 years";
    }
}

The final implementation of the Dog class.

public class Cat extends LandAnimal {
    @Override
    public String lifespan() {
        return "12 - 18 years";
    }
}

Since LandAnimal is non-sealed abstract class, it can be inherited without restriction.

public final class SeaTurtle extends AquaticAnimal {
    @Override
    public String lifespan() {
        return "Up to 50 years or more";
    }
}

SeaTurtle is the only class that can implement AquaticAnimal. Because this the only class permitted in the AquaticAnimal definition.