Skip to main content

Complete Guide to Enabling HTTPS on Apache Tomcat

When your web application moves beyond hobby status, the first hardening step is wrapping every byte in TLS. Tomcat makes the process painless once you understand where the moving parts live. In this guide you will create (or import) a certificate, wire it into Tomcat’s connector, and verify that the padlock appears in every browser. Why HTTPS on Tomcat Matters Plain HTTP exposes cookies, credentials, and payloads to anyone on the wire. Search engines penalize insecure sites, browsers now flag non-TLS pages as “Not Secure”, and compliance frameworks such as PCI-DSS simply forbid clear-text traffic. Turning on HTTPS: Encrypts data in transit Proves server identity to clients Unlocks HTTP/2 and modern protocols Keeps Google and your security team happy Prerequisites and Environment Before touching configuration files ensure: Tomcat 9.x or 10.x is installed and starts cleanly on port 8080 JAVA_HOME points to JDK 8+ (keytool comes with the JDK) OpenSSL 1.1+ if you prefer generating private keys externally Server DNS name (e.g. app.ankurm.com) resolves to the VM or container

Log4j2 Logging Levels – Complete Guide

Imagine your application's log file as a constant stream of information. In a production crisis, this stream becomes a firehose. How do you find the single critical error message in a flood of routine status updates? The answer lies in logging levels. These aren't just labels; they are the fundamental control mechanism in Log4j2. They allow developers to filter noise, pinpoint failures, and monitor application health effectively. Mastering this hierarchy is the key to creating logs that are helpful, not overwhelming. This guide explores Log4j2’s levels, from configuration to real-world best practices. The Logging Threshold: How Levels Work Think of logging levels as a gatekeeper's volume knob. Each log message you write (an "event") is assigned a level of importance, or severity. The logger itself is then configured with a threshold level. When a message arrives, the framework compares its severity to the logger's threshold. Only messages at or above the configured threshold are processed and sent to their destination (like a file or the console). This simple mechanism provides fine-grained control over your application's verbosity. You can run the exact same code in different environments and get drastically different log outputs—all without changing a single line of Java. In development, you might set the threshold low to see everything. In production, you set it high to capture only significant errors.

Understanding JUnit 5 @AfterEach Annotation with Practical Examples

This post has been merged into a single, updated guide covering both @BeforeEach and @AfterEach together, with a per-method lifecycle diagram and a JUnit 6 compatibility note: JUnit @BeforeEach and @AfterEach: The Complete Test Lifecycle Guide. Everything that was here…

Compact Strings in Java: Measured with JOL on JDK 25 (plus Compact Object Headers)

Compact Strings measured with JOL on JDK 25: one million strings weigh 89.8 MB, against 132.0 MB with -XX:-CompactStrings (31.9% less), and one euro sign per string makes them 49% bigger. Includes the Latin-1 vs UTF-16 coder byte, byte[] sizes by length, a JMH comparison and compact object headers (opt-in on JDK 25, default on JDK 27).

Java Stream API: How to Get the Last Element

Java Streams provide elegant ways to process collections, but retrieving the last element isn't as straightforward as calling a getLast() method. Since streams are designed for sequential processing without inherent indexing, developers need specific techniques to fetch the final element efficiently. This guide explores practical approaches to solve this common programming challenge. Whether you're processing large datasets or building concise functional pipelines, understanding these methods will help you write cleaner, more maintainable code. The Core Challenge Unlike List or Deque collections, Java Streams don't maintain bidirectional iteration or direct index access. Once elements pass through the pipeline, they're consumed. This design makes operations like stream().last() impossible without workarounds. However, several clever techniques leverage stream reduction and size information to achieve the desired result. Approach 1: Using reduce() Method The reduce() operation processes each element while retaining only the last one seen. This approach works beautifully for both sequential and parallel streams. import java.util.Optional; import java.util.stream.Stream; public class LastElementWithReduce { public static void main(String[] args) { Stream fruitStream = Stream.of("apple", "banana", "cherry", "date"); Optional lastElement = fruitStream.reduce((first, second) -> second); lastElement.ifPresent(element -> System.out.println("Last fruit: " + element) ); } }

Get Year, Month, Day from Date in Java

Extracting individual date components like year, month, and day is a common requirement in Java applications. Whether you're working with legacy Date objects or modern date-time APIs, this guide will show you multiple approaches to accomplish this task efficiently. 1. Using Calendar Class (Legacy Approach) Before Java 8, the Calendar class was the standard way to extract date fields from a Date object. While it's still supported, this approach is considered outdated and less intuitive than modern alternatives. import java.util.Calendar; import java.util.Date; public class DateExtractor { public static void main(String[] args) { Date currentDate = new Date(); Calendar calendar = Calendar.getInstance(); calendar.setTime(currentDate); int year = calendar.get(Calendar.YEAR); int month = calendar.get(Calendar.MONTH) + 1; // Months are 0-based int day = calendar.get(Calendar.DAY_OF_MONTH); System.out.println("Year: " + year); System.out.println("Month: " + month); System.out.println("Day: " + day); } } Note: The Calendar.MONTH field returns values from 0 (January) to 11 (December), so you need to add 1 to get the standard 1-12 month numbering.

8086 Assembly Program to Generate the Fibonacci Sequence

This blog post will walk you through an 8086 assembly program designed to generate the Fibonacci sequence. While this may sound simple, it’s an excellent example for understanding looping, arithmetic operations, and register management in assembly language. The Fibonacci Series The Fibonacci series is a sequence where each number is the sum of the two preceding ones: 0, 1, 1, 2, 3, 5, 8, 13, 21, … In this program, we’ll generate the first 10 Fibonacci numbers and store them in memory. Code ; 8086 Program to Generate 10 Fibonacci Numbers data segment COUNT equ 10 ; Number of terms to generate FIB_SERIES db COUNT dup(?) ; Array to store the series data ends code segment assume cs:code, ds:data start: ; Initialize Data Segment (DS) register mov ax, data mov ds, ax ; Use SI as a pointer to the array mov si, offset FIB_SERIES ; Seed the first two Fibonacci numbers ; Fib(0) = 0 mov byte ptr [si], 00h ; Fib(1) = 1 inc si mov byte ptr [si], 01h ; Set up loop counter. We already have 2 numbers, ; so we need to generate (COUNT - 2) more. mov cx, COUNT sub cx, 2 L1: ; AL = Fib(n-1) (e.g., [si]) mov al, [si] ; BL = Fib(n-2) (e.g., [si-1]) mov bl, [si-1] ; AL = Fib(n-1) + Fib(n-2) add al, bl ; Move pointer to next position Fib(n) inc si ; Store the new term: [si] = AL mov [si], al ; Decrement CX, loop if CX is not zero loop L1 ; Halt execution for debugging int 3 code ends end start

GraalVM Native Images for Spring Boot 4.1: The Reflection Trap, the Fix, and the Trap Inside the Fix

GraalVM native-image compiles a Spring Boot 4.1 app into a binary that starts in 0.09 seconds instead of 3.5 -- but only after a real ClassNotFoundException, a fix using the tracing agent, and a metadata-collision trap that silently undoes that fix. Rewritten with verified Boot 4.1.1 and current GraalVM versions, honest costs, and where Project Leyden fits as the non-native alternative.

Java 8: Exact Arithmetic Operations in the Math Class

This post has been merged into a newer, more comprehensive guide: Add Two Numbers in Java Without Overflow: addExact, Widening, and BigInteger. It covers the full Math "exact" arithmetic family — addExact, subtractExact, multiplyExact, incrementExact, decrementExact, negateExact, toIntExact, and absExact…

TypeScript’s Great Divide: Understanding undefined vs. null

In the world of JavaScript and TypeScript, few concepts create as much quiet confusion as undefined and null. Both seem to represent “nothing,” yet they are not the same. Misunderstanding the distinction can lead to subtle bugs and unexpected behavior. Let’s clear the air once and for all. Think of them as two different kinds of “emptiness.” One is accidental, the other is intentional. Mastering this difference is a key step toward writing cleaner, more predictable code. Meet undefined: The Sound of Silence undefined is a primitive value that TypeScript (and JavaScript) uses to signify that a variable has been declared but has not been assigned a value. It’s the default state of “not yet initialized.” It’s the system telling you, “I have a space for this, but nothing’s in it yet.”