August 15, 2025

JDK 25 - JEP 502: Stable Values (Preview)

Abstract

JDK 25 - JEP 502: Stable Values (Preview). This JEP introduces an API for stable values, which are objects that hold immutable data. Stable values are treated as constants by the JVM, enabling the same performance optimizations that are enabled by declaring a field final. Compared to final fields, however, stable values offer greater flexibility as to the timing of their initialization.

Disclaimer

This post is solely informative. Critically think before using any information presented. Learn from it but ultimately make your own decisions at your own risk.

Requirements

I did all of the work for this post using the following major technologies. You may be able to do the same thing with different technologies or versions, but no guarantees.

  • openjdk 25-ea 2025–09–16 (build 25-ea+27–3363)
  • NetBeans 25
  • Maven 3.9.6 (Bundled with NetBeans)
  • maven-compiler-plugin-3.14.0

Download

Visit my GitHub page https://github.com/mjremijan to see all of my open source projects. The code for this post is located at https://github.com/mjremijan/thoth-jdk25

Compile and Run

This is a preview API, disabled by default. To use this API in JDK 25, you must enable preview APIs:

  • Compile the program with javac --release 25 --enable-preview Main.java and run it with java --enable-preview Main
  • When using the source code launcher, run the program with java --enable-preview Main.java
  • When using jshell, start it with jshell --enable-preview

A Traditional Logger

A Logger object is a good example to introduce JEP 502: Stable Values. Ideally, creating a Logger object should have the following characteristics:

  1. Created one time only
  2. Created only when needed (lazy initialization)
  3. Immutable
  4. Optimization by the JVM (code in-lining, etc)

The traditional way to create a Logger is through a class-level final property. Listing 1 demonstrates this.

Listing 1 - Traditional Logger with Eager Initialization

package org.thoth.jdk25.jep502.main.traditional;

import java.util.logging.Logger;

public class TraditionalLoggerWithEagerInitialization {
  private static final Logger log
    = Logger.getLogger(TraditionalLoggerWithEagerInitialization.class.getName());
  
  public void service() {
      log.info("service() method start");
  }
}

This code is good, but it has eager initialization. The Logger object is created whether or not it is ever used. While creating this logger may be a fast operation, it is easy to imagine an expensive object creation composed of many different objects using external resources which either need to be created or connected to. It would be better if the initialization was on-demand (lazy). Listing 2 demonstrates this.

Listing 2 - Traditional Logger with Lazy Initialization

package org.thoth.jdk25.jep502.main.traditional;

import java.util.logging.Logger;

public class TraditionalLoggerWithLazyInitialization {
  private static Logger log;
  
  private static Logger getLog() {
    if (log == null) {
      log = Logger.getLogger(TraditionalLoggerWithLazyInitialization.class.getName()); 
    }
    return log;
  }
  
  public void service() {
      getLog().info("service() method start");
  }
}

This code implements lazy initialization, but introduces other problems. Threading is the first obvious issue. Threading potentially allows multiple loggers to be created. The possibility for multiple instances means the JVM cannot treat this object as immutable and thus cannot fully optimize its use. This may be addressed with synchronization, but mutli-threaded code is tricky to get right. This code also introduces a subtle NullPointerException issue because if the getLog() method is not used 100% of the time then it is possible the log property is null. Accessing the Logger through the getLog() method is not ideal.

Neither of these traditional ways of creating a Logger have all of the characteristics we want for initializing the object. The purpose of JEP 502 is to address these issues. Let’s start looking at JEP 502.

StableValue<M>

StableValue<M> has been introduced to the JVM in JEP 502: Stable Values (Preview). Its purpose is to:

  1. Hold immutable data
  2. Treated as constants by the JVM, enabling the same performance optimizations that are enabled by declaring a field final
  3. Offer greater flexibility as to the timing of their initialization

The goal is to have the JVM handle the creation of these objects until they are needed. Since they are immutable, the JVM is able to optimize the bytecode for execution. Listing 3 shows the most basic use of StableValue.

Listing 3 - StableValue Logger

package org.thoth.jdk25.jep502.main.proposed;

import java.util.logging.Logger;

public class StableValueLogger {
  private final StableValue<Logger> logger = StableValue.of();

  Logger getLog() {
    return logger.orElseSet(() -> Logger.getLogger(StableValueLogger.class.getName()));
  }

  public void service() {
    getLog().info("service() method start");
  }
}

On line 6, see that the logger property is no longer a Logger instance but a StableValue<Logger> instance instead. The getLog() method calls the StableValue.orElseSet() method. The supplied lambda to this method knows how to create the Logger when needed. The orElseSet() method guarantees the lambda is evaluated only once even in a multi-threaded environment.

While using StableValue<Logger> results in a guaranteed single instance and thread safety, it unfortunately means accessing the Logger through the getLog() method to initialize the Logger object and return it. If getLog() is not used, there is no risk of a NullPointerException, but, using the StableValue API methods directly every time a Logger was needed would be very verbose and ugly. It would be more convenient if the logger property could be used more directly. This can be accomplished using a stable supplier. Listing 4 demonstrates this.

Listing 4 - Stable Supplier Logger

package org.thoth.jdk25.jep502.main.proposed;

import java.util.function.Supplier;
import java.util.logging.Logger;

public class StableSupplierLogger {
  private final Supplier<Logger> logger 
    = StableValue.supplier(() -> Logger.getLogger(StableSupplierLogger.class.getName()));

  public void service() {
    logger.get().info("service() method start");
  }
}

On line 7, see that the logger property is no longer a Logger instance but a Supplier<Logger> instance instead. The lambda passed to StableValue.supplier() is responsible for creating a Logger when evaluated.

On line 11, see that the Supplier.get() method is called. Similar to the orElseSet() method, the lambda is guaranteed to evaluate only once even in a multi-threaded environment. While using the Supplier.get() method is still an intermediary step needed to get the Logger, using this instance method is a better user experience than using a custom private getLog() method.

Summary

This has been a quick look into JEP 502: Stable Values (Preview). It is a new API, giving the JVM the ability to manage the creation of immutable objects. Having the JVM manage the creation of the objects conserves resources by creating objects only when needed and optimizes performance by treating them as final constant values. This is a preview feature so look for it to be finalized in a future release.

References

Minborg, P., Cimadamore, M. (2023, July 24). JEP 502: Stable Values (Preview). https://openjdk.org/jeps/502.

July 29, 2025

JDK 25 - JEP 470: PEM Encodings of Cryptographic Objects (Preview)

Abstract

JDK 25 - JEP 470: PEM Encodings of Cryptographic Objects (Preview). The Privacy-Enhanced Mail (PEM) format https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail defined by RFC 7468 https://www.rfc-editor.org/rfc/rfc7468 is a representation of cryptographic objects. This JEP adds encoding and decoding of the PEM format to Java.

Disclaimer

This post is solely informative. Critically think before using any information presented. Learn from it but ultimately make your own decisions at your own risk.

Requirements

I did all of the work for this post using the following major technologies. You may be able to do the same thing with different technologies or versions, but no guarantees.

  • openjdk 25-ea 2025–09–16 (build 25-ea+27–3363)
  • NetBeans 25
  • Maven 3.9.6 (Bundled with NetBeans)
  • maven-compiler-plugin-3.14.0

Download

Visit my GitHub page https://github.com/mjremijan to see all of my open source projects. The code for this post is located at https://github.com/mjremijan/thoth-jdk25

Compile and Run

This is a preview API, disabled by default. To use this API in JDK 25, you must enable preview APIs:

  • Compile the program with javac --release 25 --enable-preview Main.java and run it with java --enable-preview Main;
  • When using the source code launcher, run the program with java --enable-preview Main.java;
  • When using jshell, start it with jshell --enable-preview.

Java Generate public/private PEM files

Create a Java KeyPair and generate the public/private keys in PEM format.

Listing 1 - Java Generate Keys in PEM Format

package org.thoth.jdk25.jep470.main;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PEMEncoder;
import java.security.SecureRandom;

/**
 */
public class MainInMemoryEncoding {

  public static void main(String[] args) throws Exception {
    System.out.printf("Hello JEP-470 In memory encoding!%n%n");
    
    System.out.printf("Create KeyPairGenerator for RSA%n");
    KeyPairGenerator generator 
      = KeyPairGenerator.getInstance("RSA");
    
    System.out.printf("Initialize KeyPairGenerator with a key size and SecureRandom%n");
    generator.initialize(2048, new SecureRandom());
    
    System.out.printf("Generate a KeyPair%n");
    KeyPair pair
      = generator.generateKeyPair();
    
    System.out.printf("Create PEMEncoder%n");
    PEMEncoder pe = PEMEncoder.of();
    
    System.out.printf("Encode private key to the PEM format%n");
    String privatePem = pe.encodeToString(pair.getPrivate());
    System.out.printf("%s%n", privatePem);
    
    System.out.printf("Encode public key to the PEM format%n");
    String publicPem = pe.encodeToString(pair.getPublic());
    System.out.printf("%s%n", publicPem);
  }
}

OpenSSL Generate public/private PEM files

Use the following Linux commands to generate public/private keys in PEM format saved to files.

Listing 2 - Generate 2048-bit RSA private key PEM file

openssl genrsa -out test-private.pem 2048

Listing 3 - Extract RSA public key PEM file

openssl rsa -in test-private.pem -out test-public.pem -pubout -outform PEM

Java read public OpenSSL PEM file

Read the test-public.pem file.

Listing 4 - Read test-public.pem

package org.thoth.jdk25.jep470.main;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.DEREncodable;
import java.security.PEMDecoder;
import java.security.PublicKey;

/**
 */
public class MainOpenSSLDecodingPublicFile {

  public static void main(String[] args) throws Exception {
    System.out.printf("Hello JEP-470! OpenSSL decoding public PEM file%n%n");
    
    System.out.printf("Read the OpenSSL test-public.pem file into a String%n");    
    InputStream is = MainOpenSSLDecodingPublicFile.class.getClassLoader().getResourceAsStream("test-public.pem");
    byte[] bytes = is.readAllBytes();
    String publicPem = new String(bytes, StandardCharsets.UTF_8);
    System.out.printf("%s%n", publicPem);
    
    System.out.printf("Create PEMDecoder%n");
    PEMDecoder pd = PEMDecoder.of();

    System.out.printf("Decode the OpenSSL test-public.pem file%n");
    DEREncodable derEncodable = pd.decode(publicPem);
    switch (derEncodable) {
      case PublicKey publicKey -> System.out.printf("Successfully decoded PublicKey!%n");  
      default -> System.out.printf("What is \"%s\"%n", derEncodable.getClass().getName());
    }      
  }
}

Java read private OpenSSL PEM file

Read the test-private.pem file.

Listing 5 - Read test-private.pem

package org.thoth.jdk25.jep470.main;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.DEREncodable;
import java.security.PEMDecoder;
import java.security.PrivateKey;

/**
 */
public class MainOpenSSLDecodingPrivateFile {
    public static void main(String[] args) throws Exception {
     System.out.printf("Hello JEP-470! OpenSSL decoding private PEM file%n%n");
     
     System.out.printf("Read the OpenSSL test-private.pem file into a String%n");
     InputStream is = MainOpenSSLDecodingPrivateFile.class.getClassLoader().getResourceAsStream("test-private.pem");
     byte[] bytes = is.readAllBytes();
     String privatePem = new String(bytes, StandardCharsets.UTF_8);
     System.out.printf("%s%n", privatePem);

     System.out.printf("Create PEMDecoder%n");
     PEMDecoder pd = PEMDecoder.of();
     
     System.out.printf("Decode the OpenSSL test-private.pem file%n");
     DEREncodable derEncodable = pd.decode(privatePem);
     switch (derEncodable) {
      case PrivateKey privateKey -> System.out.printf("Successfully decoded PrivateKey!%n");    
      default -> System.out.printf("What is \"%s\"%n", derEncodable.getClass().getName());
     }
  }
}

Summary

JEP 470 add PEM encoding and decoding functionality to Java. In JDK 25, it is a preview feature so look for it to be finalized in a future release.

References

Scarpino, A. (2023, January 23). JEP 470: PEM Encodings of Cryptographic Objects (Preview). https://openjdk.org/jeps/470

May 07, 2025

Email Notes to OneNote with IFTTT

Abstract

Microsoft officially retired the me@onenote.com email feature on March 26, 2025. This service previously allowed users to forward emails directly into their OneNote notebooks by sending them to that address.

The decision to discontinue this feature was due to low usage and the availability of more integrated alternatives. Microsoft recommends using the “Send to OneNote” feature within Outlook, which allows users to send emails and meeting invitations directly to specific OneNote notebooks and sections.

Unfortunately, I am one of those “low usage” people. I relied quite heavily on the me@onenote.com email feature. Using the “Send to OneNote” feature within Outlook of course is not remotely the same thing and an attempt by Microsoft to force people to Outlook for email.

Fortunately, I found a very simple and easy solution: IFTTT

Disclaimer

This post is solely informative. Critically think before using any information presented. Learn from it but ultimately make your own decisions at your own risk.

Requirements

I did all of the work for this post using the following IFTTT Applets available as of May 7, 2025.

  • “Forward important emails to OneNote”

Add and Configure IFTTT Applet

Login to IFTTT

Click the “Explore” button

Search for “Forward important emails to OneNote”. It is an IFTTT Applet that looks like this:

Figure 1 - IFTTT Applet

IFTTT Applet
IFTTT Applet

Click “Connect” to get started with configuring this applet.

Figure 2 - IFTTT Connect Button

IFTTT Connect Button
IFTTT Connect Button

Once it is connected, configure the OneNote part of the applet so a new note is created in the Notebook and Section you want. In the example below, New notes will be created in my “Process” notebook inside the “Collect” section.

Figure 3 - IFTTT OneNote Configuration

IFTTT OneNote Configuration
IFTTT OneNote Configuration

Once this is all done, send an email to “trigger@applet.ifttt.com” and emails will show up in OneNote.

Summary

Use IFTTT to fill in the gap with Microsoft ending support for me@onenote.com.

February 04, 2025

arch42 Stakeholders for Software

Abstract

arch42 has a section to document the stakeholders of the platform. Stakeholders have different roles and expectations when interacting with the platform’s architecture documentation. The purpose of this post to describe a few common roles and how they relate to each other.

Disclaimer

This post is solely informative. Critically think before using any information presented. Learn from it but ultimately make your own decisions at your own risk.

Requirements

A solution architect documenting the roles and expectations of stakeholders for the platform.

Roles

There are many people involved with the platform. Each person has a particular role and expectation with respect to the platform architecture and its documentation. People may have many roles. ChatGPT was initially prompted with “in software development if the analyst owns the what and the architect owns the how, what does the developer own?”. It was then prompted with the follow-ups “what does the tester own?” and “what does the user own?”. Responses were used to describe roles and expectations.

Owner. The owner is expected to provide the platform team decisions about (a) underlying business goals, (b) platform features and functional (business) requirements, and (c) platform architecture and non-functional (quality) requirements.

Analyst. The analyst is expected to provide the platform team with fully documented functional (business) requirements of the platform features in fulfillment of the underlying business goals. The analyst owns the “what” of the platform’s operation and maintains it in the architecture documentation.

Architect. The architect is expected to provide the platform team with fully documented non-functional (quality) requirements of the platform features in fulfillment of the underlying quality goals. The architect owns the “how” of the platform’s operation and maintains it in the architecture documentation.

Developer. The developer is expected to provide the platform team with an implementation of the platform in fulfillment of both the functional (business) and non-functional (quality) requirements specified by the analyst and architect respectively. The developer owns the “do” of the platform’s operation and references the architecture documentation.

Tester. The tester is expected to provide the platform team with quality assurance of the platform in fulfillment of both the functional (business) and non-functional (quality) requirements specified by the analyst and architect respectively. The tester owns the “validate” of the platform’s operation and references the architecture documentation.

User. The user is expected to provide the platform team with feedback on the platform. The user owns the “experience” of the platform’s operation with no direct use of the architecture documentation.

No list like this can be exhaustive, but, for most platforms this is a good list of roles to start with. Update the expectations or add new roles as appropriate for your platform.

Summary

That’s it, enjoy!

References

arc42 Documentation. (n.d.). https://docs.arc42.org/home/

ISO 25010. (n.d.). https://iso25000.com/index.php/en/iso-25000-standards/iso-25010

OpenAI. (2025). ChatGPT (Feb 04 version) [Large language model]. https://chatgpt.com/