How do I monitor audio levels in real time using Java Sound API?

Monitoring audio levels in real time is useful for applications like voice recorders, streaming tools, or any app that displays a volume meter. In Java, this is possible using the javax.sound.sampled package, specifically with the TargetDataLine class.

In this post, you’ll learn how to:

  • Capture audio input from a microphone
  • Convert it into byte data
  • Calculate the current audio level (amplitude)
  • Display the level in real time (console bar graph style)

Step 1: Setup Required Imports

import javax.sound.sampled.*;

Step 2: Open the Microphone (TargetDataLine)

You’ll need to configure and open a TargetDataLine with a supported audio format:

AudioFormat format = new AudioFormat(44100.0f, 16, 1, true, true);
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);

TargetDataLine line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format);
line.start();

Step 3: Read and Analyze Audio Data in Real Time

We’ll continuously read short chunks of audio and calculate the volume level based on the root-mean-square (RMS) of the signal.

byte[] buffer = new byte[1024];
int bytesRead;

System.out.println("Monitoring audio levels... (Ctrl+C to stop)");

while (true) {
    bytesRead = line.read(buffer, 0, buffer.length);

    // Convert bytes to amplitude
    double sum = 0.0;
    for (int i = 0; i < bytesRead; i += 2) {
        // Convert byte pair to int
        int sample = (buffer[i] << 8) | (buffer[i + 1] & 0xFF);
        sum += sample * sample;
    }

    double rms = Math.sqrt(sum / ((double) bytesRead / 2));
    double db = 20 * Math.log10(rms); // Convert to decibels

    // Visualize as a simple bar
    int level = (int) (db + 50); // Normalize range
    level = Math.max(0, Math.min(50, level));
    System.out.println("[" + "*".repeat(level) + "]");
}

Step 4: Clean Up

You should close the audio line when you’re done:

line.stop();
line.close();

Notes and Tips

  • The audio input format is 44.1 kHz, 16-bit, mono, signed, big-endian. You can change it to suit your needs.
  • The loop runs indefinitely. You may want to run it on a background thread and provide a stop condition.
  • For better GUI visualization, consider integrating with Swing or JavaFX.

Summary

You’ve just created a simple Java program that listens to microphone input and prints real-time audio level feedback. This can be used as the foundation for:

  • Voice activity detection
  • Audio visualizers
  • Mute detection
  • Noise level meters

How to Delete Files and Directories Recursively in Java

In Java, you can delete files and directories recursively by writing a utility method. The strategy involves:

  1. Checking if a file is a directory: If it is, then recursively delete its contents first.
  2. Deleting the file or directory: After ensuring a directory is empty, delete it.

Here’s an example implementation:

Recursive Deletion of Files and Directories

import java.io.File;

public class FileDeleter {
    public static void deleteRecursively(File fileOrDirectory) {
        if (fileOrDirectory.isDirectory()) {
            // Recursively delete contents of the directory
            for (File child : fileOrDirectory.listFiles()) {
                deleteRecursively(child);
            }
        }
        // Delete the file or empty directory
        if (!fileOrDirectory.delete()) {
            System.err.println("Failed to delete: " + fileOrDirectory.getAbsolutePath());
        }
    }

    public static void main(String[] args) {
        // Example directory to delete
        File directoryToDelete = new File("path/to/directory");

        if (directoryToDelete.exists()) {
            deleteRecursively(directoryToDelete);
            System.out.println("Deletion completed.");
        } else {
            System.out.println("Directory does not exist.");
        }
    }
}

How This Code Works

  1. Check if it’s a directory (fileOrDirectory.isDirectory()):
    • If true, invoke the method recursively on its child files/directories.
  2. Delete files or empty directories:
    • Once all contents of a directory are deleted, the directory itself is deleted using fileOrDirectory.delete().

Things to Keep in Mind

  • Permissions: Ensure your program has the necessary permissions to delete the files or directories.
  • Error Handling: The delete() method returns false if the deletion failed, so handle errors accordingly.
  • Symbolic Links: isDirectory() will follow symbolic links. Handle them carefully if your system contains symbolic links to prevent undesired deletions.

Example Usage

// Delete a directory recursively
FileDeleter.deleteRecursively(new File("path/to/directory"));

// Delete a single file
FileDeleter.deleteRecursively(new File("path/to/file.txt"));

This approach ensures that all the files and subdirectories inside a directory are deleted before the directory itself is removed.

How to Visualize System Components with PlantUML Component Diagrams

To visualize system components using PlantUML Component Diagrams, you’ll need to follow these steps. Component diagrams allow you to model and delineate the architecture of a larger system by showing how components interact with each other.

Steps to Create a Component Diagram with PlantUML

  1. Set Up PlantUML
    To create component diagrams with PlantUML, you need:

    • Java installed
    • PlantUML jar file (or an IDE/plugin with integrated PlantUML support like IntelliJ or VSCode with the PlantUML extension)
    • A rendering tool such as Graphviz (dot).
  2. Start the Diagram
    Specify the start of the diagram using:

    @startuml
    
  3. Define Components
    Each component in the system can be represented with the component keyword. Give each component a meaningful name. Use square brackets or the as keyword to assign aliases/titles to the components:

    component [Component A]
    component "Database" as DB
    
  4. Show Relationships Between Components
    Use arrows (--> or --) to represent interfaces, dependencies, or flows between components:

    [Frontend] --> [Backend]
    [Backend] --> DB
    
  5. Group Components (Optional)
    Use package to group logically related components:

    package "User Interface" {
       [Frontend]
       component "Authentication Module" as AuthModule
    }
    
  6. Icons for Common Elements (Optional)
    You can use PlantUML’s built-in stereotypes to enhance clarity by showing commonly used icons:

    component [Cloud Service] <<cloud>>
    component [Database] <<database>>
    
  7. End the Diagram
    Close the diagram with:

    @enduml
    

Example: Simple Component Diagram

Here’s a complete example that shows an e-commerce system with a frontend, backend, and database:

@startuml
title E-Commerce System Architecture

package "User Interface" {
    [Frontend]
}

package "Business Logic" {
    component "Authentication Service" as AuthService
    component "Product Service" as ProductService
}

package "Data Layer" {
    [Database] <<database>>
}

[Frontend] --> AuthService : authenticate()
[Frontend] --> ProductService : fetch products
AuthService --> [Database] : verify credentials
ProductService --> [Database] : query data

@enduml

Render the Diagram

  • Run the PlantUML jar file or use an IDE plugin to generate the component diagram as an image (PNG, SVG, etc.).
  • Use online tools such as PlantUML Server or integrated plugins in IDEs.

Output

The diagram will illustrate:

  • Frontend interacting with services in the backend.
  • Backend services communicating with the database.
  • Logical groupings (packages) of components.

By following these steps, you can easily model and abstract complex systems to identify dependencies, cohesion, and interactions clearly.

How to Design Package Diagrams Using PlantUML for Modular Architecture

In a modular architecture, package diagrams are a powerful way to represent the dependencies and relationships between different modules or packages within a system. With PlantUML, you can easily create package diagrams to visually describe your architecture and ensure modularity principles like separation of concerns, low coupling, and high cohesion are maintained.
Here’s how you can design package diagrams using PlantUML for modular architecture:

1. Understanding the Components of Package Diagrams

Before creating the diagram, it’s important to understand the following key elements:

  • Packages: Represent logical groupings of classes, modules, or functionalities.
  • Dependencies: Links between packages show directional relationships (e.g., which package depends on another).
  • Hierarchies: You can nest packages inside others to show submodules or grouped components.

2. Basic PlantUML Syntax for Package Diagrams

PlantUML provides simple syntax for creating package diagrams using keywords like package, namespace, and component.

Example Syntax:

@startuml
package "Module 1" {
  [Class1]
  [Class2]
}

package "Module 2" {
  [Class3]
}

[Class1] --> [Class3] : Uses
@enduml

3. Steps for Designing Modular Architecture Package Diagram

Follow these steps to design a package diagram for modular architecture:

Step 1: Identify Modules or Layers

List all high-level modules or layers of your system (e.g., UI Layer, Business Logic Layer, Data Access Layer, etc.).

Step 2: Define Submodules

Break each module into its submodules or components (e.g., User Management Module inside Business Logic Layer).

Step 3: Show Dependencies

Draw directional relationships between modules. Ensure dependencies only flow in one direction to avoid circular links.

Step 4: Apply Abstractions

Use abstractions like interfaces and package hierarchy to reduce direct dependencies between modules.

4. PlantUML Example: Modular Architecture

Here’s an example of a modular architecture package diagram using PlantUML:

@startuml
title Modular Architecture Package Diagram

package "UI Layer" {
  [LoginScreen]
  [Dashboard]
}

package "Business Logic Layer" {
  [UserService]
  [OrderService]
}

package "Data Access Layer" {
  [UserRepository]
  [OrderRepository]
}

[LoginScreen] --> [UserService] : Uses
[Dashboard] --> [OrderService] : Displays Data
[UserService] --> [UserRepository] : Accesses Data
[OrderService] --> [OrderRepository] : Accesses Data

@enduml

This example demonstrates:

  1. Abstract layers to separate responsibilities (UI, Business Logic, Data Access).
  2. Directional dependencies to reduce coupling.
  3. Components grouped logically by their roles.

5. Advanced Features

PlantUML allows you to incorporate advanced features in package diagrams:

  • Nested Packages: Nest submodules within a parent module to show hierarchical relationships.
  @startuml
  package "Business Logic Layer" {
    package "User Management" {
      [UserService]
      [UserValidator]
    }

    package "Order Management" {
      [OrderService]
      [OrderValidator]
    }
  }
  @enduml
  • Styling Packages: You can customize the styles for better visuals.
  @startuml
  package "Module A" #LightBlue {
    [Component1]
    [Component2]
  }

  package "Module B" #LightGreen {
    [Component3]
  }

  [Component1] --> [Component3]
  [Component2] --> [Component3]
  @enduml
  • Interfaces in Packages: Use interface to show exposed functionality.
  @startuml
  package "Business Logic Layer" {
    interface IOrderService
    [OrderService]
    IOrderService <|.. [OrderService]
  }

  [UI] --> IOrderService
  @enduml

6. Best Practices for Modular Architecture

  • Minimize Coupling: Ensure packages communicate only via interfaces or well-defined dependencies.
  • High Cohesion: Group related functionalities together in the same package.
  • Avoid Circular Dependencies: Acyclic dependencies promote better maintainability.
  • Group by Layers: Prefer logical layers (e.g., presentation, domain, infrastructure).
  • Add Descriptions: Use notes for additional descriptions.

7. Tools for Generating Package Diagrams

You can generate diagrams directly from PlantUML-text files or integrate with tools like:

  • IntelliJ IDEA (with PlantUML plugin)
  • Visual Studio Code (with PlantUML extension)
  • Online tools like PlantUML Editor

By following these practices and using the examples, you can effectively design modular architecture package diagrams using PlantUML.

How to Create a Custom Date Comparator in Java

To create a custom date comparator in Java, you can follow these steps:

1. Understand the Requirements

A date comparator is used to sort objects based on date values. For instance, consider a User class that has a Date field (e.g., ). We’ll compare and sort User instances by that date. birthDate

2. Define a Custom Comparator

In Java, you can create a Comparator by implementing the compare method or using lambda expressions along with the Comparator utility.

Example Code for Custom Date Comparator:

Here’s an example of creating a custom date comparator for sorting objects by date:

import java.util.*;
import java.util.stream.Stream;
import java.text.SimpleDateFormat;

public class CustomDateComparatorExample {

    public static void main(String[] args) throws Exception {

        // Sample date format and users with dates
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Stream<User> usersStream = Stream.of(
                new User("John", dateFormat.parse("1993-05-12")),
                new User("Rose", dateFormat.parse("1994-11-28")),
                new User("Adam", dateFormat.parse("1987-07-15"))
        );

        // Custom Date Comparator using Comparator.comparing
        usersStream
                .sorted(Comparator.comparing(User::getBirthDate))
                .forEach(System.out::println);
    }

    static class User {
        String name;
        Date birthDate;

        User(String name, Date birthDate) {
            this.name = name;
            this.birthDate = birthDate;
        }

        String getName() {
            return name;
        }

        Date getBirthDate() {
            return birthDate;
        }

        @Override
        public String toString() {
            return "User{" + "name='" + name + '\'' +
                    ", birthDate=" + birthDate + '}';
        }
    }
}

Explanation:

  1. Date Field ()birthDate:
    • Replaced age with a Date field () in the User class to sort based on dates. birthDate
  2. Custom Comparator:
    • Used Comparator.comparing() to directly compare the field. birthDate
    • It simplifies creating a comparator for a specific field, which in this case is a Date object.
  3. Sorted Stream:
    • The Stream.sorted() function is applied with our custom comparator. It ensures the stream of User objects is sorted.

Alternative: Manually Implement the Comparator

You can define the comparator manually for more control:

// Custom Comparator Implementation
Comparator<User> dateComparator = new Comparator<User>() {
    @Override
    public int compare(User u1, User u2) {
        return u1.getBirthDate().compareTo(u2.getBirthDate());
    }
};

// Usage
usersStream.sorted(dateComparator).forEach(System.out::println);

This is especially useful if you need more complex comparison logic (e.g., null handling or multi-level comparison).

Points to Remember:

  • Null Safety: Always handle null dates to avoid. Use Comparator.nullsFirst() or Comparator.nullsLast() when necessary. NullPointerException
    Comparator.comparing(User::getBirthDate, Comparator.nullsFirst(Date::compareTo));
    
  • Custom Date Format: Adjust the date format as needed using SimpleDateFormat, LocalDate, or other relevant classes from java.time.
    With this knowledge, you can tailor the comparator to fit any specific user-defined date or object sorting needs!

How to Use TreeMap for Sorted Key Access in Java

The TreeMap class in Java is part of the java.util package and provides an implementation of the Map interface that keeps its keys sorted in a natural order (according to ) or a custom order (defined by a Comparator, if provided during construction)Comparable. It’s commonly used when you need to access keys in sorted order efficiently.
Here’s a guide on how to use TreeMap for sorted key access in Java:

Key Features of TreeMap

  1. Maintains sorted order of keys.
  2. Implements the SortedMap and NavigableMap interfaces.
  3. Operates based on a Red-Black Tree, ensuring efficient sorting and lookup (O(log n) for most operations).

Basic Usage

Follow these steps to use TreeMap for sorted key access:

1. Create a TreeMap

You can create a TreeMap object with or without a custom comparator.

import java.util.*;

public class TreeMapExample {
    public static void main(String[] args) {
        // Natural ordering (keys must implement Comparable)
        TreeMap<Integer, String> treeMap = new TreeMap<>();

        // Custom comparator (e.g., descending order)
        TreeMap<Integer, String> customTreeMap = new TreeMap<>(Comparator.reverseOrder());
    }
}

2. Add Key-Value Pairs

Adding elements to a TreeMap is straightforward, using the put() method.

treeMap.put(3, "Three");
treeMap.put(1, "One");
treeMap.put(2, "Two");

The elements will automatically be stored in ascending order of keys.

3. Iterate Over Sorted Entries

The entries in the TreeMap can be accessed in sorted order.

for (Map.Entry<Integer, String> entry : treeMap.entrySet()) {
    System.out.println(entry.getKey() + " -> " + entry.getValue());
}

Output:

1 -> One
2 -> Two
3 -> Three

4. Access Specific Portions of the Map

The TreeMap provides powerful methods to access subsets of keys and values:

  • headMap(K toKey, boolean inclusive): Get keys less than a given key.
  • tailMap(K fromKey, boolean inclusive): Get keys greater than a given key.
  • subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive): Get keys in a given range.

Example:

System.out.println("Keys less than 3: " + treeMap.headMap(3).keySet());
System.out.println("Keys greater than or equal to 2: " + treeMap.tailMap(2).keySet());
System.out.println("Keys between 1 (inclusive) and 3 (exclusive): " 
                   + treeMap.subMap(1, true, 3, false).keySet());

Output:

Keys less than 3: [1, 2]
Keys greater than or equal to 2: [2, 3]
Keys between 1 (inclusive) and 3 (exclusive): [1, 2]

5. Use NavigableMap Methods

The TreeMap also implements the NavigableMap interface, offering methods for navigation:

  • firstKey() / lastKey(): Get the smallest/largest key.
  • lowerKey(key) / higherKey(key): Get the keys just below/above a given key.
  • floorKey(key) / ceilingKey(key): Get keys less than/greater than or equal to the given key.

Example:

System.out.println("First key: " + treeMap.firstKey());
System.out.println("Last key: " + treeMap.lastKey());
System.out.println("Key just below 3: " + treeMap.lowerKey(3));
System.out.println("Key just above 2: " + treeMap.higherKey(2));

Output:

First key: 1
Last key: 3
Key just below 3: 2
Key just above 2: 3

6. Remove Items

You can remove specific entries using the remove(key) method.

treeMap.remove(2); // Removes the key "2"
System.out.println(treeMap);

Output:

{1=One, 3=Three}

Example: Full Program

package org.kodejava.util;

import java.util.*;

public class TreeMapExample {
    public static void main(String[] args) {
        // Create a TreeMap
        TreeMap<Integer, String> treeMap = new TreeMap<>();

        // Add elements
        treeMap.put(3, "Three");
        treeMap.put(1, "One");
        treeMap.put(2, "Two");

        // Iterate over TreeMap
        System.out.println("TreeMap in ascending order:");
        for (Map.Entry<Integer, String> entry : treeMap.entrySet()) {
            System.out.println(entry.getKey() + " -> " + entry.getValue());
        }

        // Access portions of the map
        System.out.println("Keys less than 2: " + treeMap.headMap(2).keySet());
        System.out.println("Keys greater than or equal to 2: " + treeMap.tailMap(2).keySet());

        // Use NavigableMap methods
        System.out.println("First key: " + treeMap.firstKey());
        System.out.println("Last key: " + treeMap.lastKey());
    }
}

Output:

TreeMap in ascending order:
1 -> One
2 -> Two
3 -> Three
Keys less than 2: [1]
Keys greater than or equal to 2: [2, 3]
First key: 1
Last key: 3

Things to Remember

  1. Keys must be Comparable or you must provide a Comparator during construction.
  2. Null keys are not allowed in TreeMap, but null values are permitted.
  3. Use TreeMap when you need sorted access; otherwise, HashMap is a better choice for performance.

How to Encode and Decode URLs in Java

In Java, you can encode and decode URLs using the java.net.URLEncoder and java.net.URLDecoder classes. These classes handle encoding and decoding in compliance with the application/x-www-form-urlencoded MIME type.
Here’s how you can encode and decode URLs:

Code Example

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.net.URLDecoder;

public class URLEncoderDecoderExample {

    public static void main(String[] args) {
        try {
            // The String to be encoded
            String url = "https://example.com/query?name=John Doe&age=25";

            // Encoding URL
            String encodedUrl = URLEncoder.encode(url, "UTF-8");
            System.out.println("Encoded URL: " + encodedUrl);

            // Decoding URL
            String decodedUrl = URLDecoder.decode(encodedUrl, "UTF-8");
            System.out.println("Decoded URL: " + decodedUrl);

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace(); // Handle exception if unsupported encoding is provided
        }
    }
}

Explanation:

  1. Encoding:
    • The URLEncoder.encode() method encodes special characters in the URL to make it safe for transmission over the network.
    • UTF-8 is typically used as the charset.
  2. Decoding:
    • The URLDecoder.decode() method decodes the string back to its original format.

Sample Output:

If the input is:

https://example.com/query?name=John Doe&age=25

After encoding:

https%3A%2F%2Fexample.com%2Fquery%3Fname%3DJohn+Doe%26age%3D25

After decoding:

https://example.com/query?name=John Doe&age=25

Notes:

  • Replace spaces with + in the encoded string. This is because spaces are not typically allowed in URLs, and encoding replaces them.
  • Use "UTF-8" because it’s the most widely used and supports all Unicode characters.

How to Resolve a Domain Name in Java

Here are common ways to resolve domain names in Java, from simplest to more advanced use cases.

Basic A/AAAA record lookup (IPv4/IPv6)

  • Uses the system resolver and OS DNS settings.
  • Returns all IPs (both IPv4 and IPv6 where available).
import java.net.InetAddress;
import java.net.UnknownHostException;

public class DnsLookup {
    public static void main(String[] args) {
        String host = "example.com";
        try {
            InetAddress[] addresses = InetAddress.getAllByName(host);
            for (InetAddress addr : addresses) {
                System.out.println(addr.getHostAddress());
            }
        } catch (UnknownHostException e) {
            System.err.println("DNS lookup failed: " + e.getMessage());
        }
    }
}

Notes for InetAddress:

  • No direct per-call timeout configuration (it relies on OS resolver timeouts).
  • Caching is controlled by security properties:
    • -Dnetworkaddress.cache.ttl=60 (seconds; -1 = forever; default often JVM-dependent)
    • -Dnetworkaddress.cache.negative.ttl=10
  • Prefer IPv6: -Djava.net.preferIPv6Addresses=true

Asynchronous lookups

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.concurrent.CompletableFuture;

public class AsyncDns {
    public static CompletableFuture<InetAddress[]> resolve(String host) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                return InetAddress.getAllByName(host);
            } catch (UnknownHostException e) {
                throw new RuntimeException(e);
            }
        });
    }
}

Reverse DNS (PTR)

  • Basic: addr.getHostName() may trigger reverse lookup (can be slow or cached).
InetAddress addr = InetAddress.getByName("93.184.216.34");
String reverse = addr.getHostName(); // may do a PTR lookup

Query specific DNS record types (MX, TXT, SRV, PTR) or specific DNS servers

Option 1: JNDI DNS (built-in, configurable)

import javax.naming.directory.*;
import javax.naming.*;
import java.util.Hashtable;

public class JndiDns {
    public static void main(String[] args) throws NamingException {
        String domain = "example.com";
        Hashtable<String, String> env = new Hashtable<>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");
        // Use specific DNS server(s) (optional)
        env.put(Context.PROVIDER_URL, "dns://8.8.8.8 dns://1.1.1.1");
        // Timeouts in milliseconds (optional)
        env.put("com.sun.jndi.dns.timeout.initial", "2000");
        env.put("com.sun.jndi.dns.timeout.retries", "1");

        DirContext ctx = new InitialDirContext(env);
        Attributes attrs = ctx.getAttributes(domain, new String[] {"MX", "TXT", "A"});
        Attribute mx = attrs.get("MX");
        if (mx != null) {
            for (int i = 0; i < mx.size(); i++) System.out.println("MX: " + mx.get(i));
        }
        Attribute txt = attrs.get("TXT");
        if (txt != null) {
            for (int i = 0; i < txt.size(); i++) System.out.println("TXT: " + txt.get(i));
        }
        Attribute a = attrs.get("A");
        if (a != null) {
            for (int i = 0; i < a.size(); i++) System.out.println("A: " + a.get(i));
        }
    }
}

Notes:

  • JNDI DNS supports MX, TXT, SRV, CNAME, PTR, etc.
  • You can set specific DNS servers via PROVIDER_URL.

Option 2: Use a dedicated DNS library (e.g., dnsjava)

  • Recommended for fine control, timeouts, EDNS, DNSSEC (if needed), or custom resolvers.

Maven Dependency:

<dependency>
    <groupId>dnsjava</groupId>
    <artifactId>dnsjava</artifactId>
    <version>3.6.3</version>
    <type>bundle</type>
</dependency>

Lookup A/AAAA with custom resolver and timeout:

import org.xbill.DNS.*;

public class DnsJavaExample {
    public static void main(String[] args) throws Exception {
        String domain = "example.com";
        Resolver resolver = new SimpleResolver("8.8.8.8");
        resolver.setTimeout(Duration.ofSeconds(2));
        Name name = Name.fromString(domain + ".");
        Record[] records = new Lookup(name, Type.A).run();
        if (records != null) {
            for (Record r : records) System.out.println(r.rdataToString());
        }
    }
}

SRV/TXT example:

import org.xbill.DNS.*;

Name srvName = Name.fromString("_sip._tcp.example.com.");
Record[] srv = new Lookup(srvName, Type.SRV).run();
if (srv != null) {
    for (Record r : srv) System.out.println(r.rdataToString());
}

Name txtName = Name.fromString("example.com.");
Record[] txt = new Lookup(txtName, Type.TXT).run();
if (txt != null) {
    for (Record r : txt) System.out.println(r.rdataToString());
}

Spring/Jakarta usage example (service component)

import org.springframework.stereotype.Service;
import java.net.InetAddress;

@Service
public class DnsService {
    public String[] resolve(String host) {
        try {
            return java.util.Arrays.stream(InetAddress.getAllByName(host))
                    .map(InetAddress::getHostAddress)
                    .toArray(String[]::new);
        } catch (Exception e) {
            return new String[0];
        }
    }
}

Practical tips

  • Retry logic: DNS failures are often transient. Consider simple retries with backoff when appropriate.
  • Validate input: Ensure the host is a valid hostname to avoid unnecessary exceptions.
  • Respect caching: Tune networkaddress.cache.ttl for your runtime environment to balance freshness and performance.
  • Split-horizon DNS: In containerized/cloud setups, behavior may differ between environments. Test where it runs.
  • Don’t hardcode IPs unless necessary; prefer hostnames to benefit from DNS-based failover.

How to Get Hostname and IP Address in Java

Here are the most common and reliable ways to get hostnames and IP addresses in Java (Java 21). Pick the approach that matches your runtime (desktop app, server app, behind proxy, etc.).

  1. Quick local host info
    • Good for simple cases, but can return 127.0.0.1 if your host isn’t configured in DNS/hosts.
    import java.net.InetAddress;
    
    public class LocalHostQuick {
        public static void main(String[] args) throws Exception {
            InetAddress local = InetAddress.getLocalHost();
            System.out.println("Host name: " + local.getHostName());
            System.out.println("Canonical host name: " + local.getCanonicalHostName());
            System.out.println("IP address: " + local.getHostAddress());
        }
    }
    
  2. Robust way: list network interfaces
    • Picks non-loopback, non-virtual, up interfaces; prefers IPv4 but supports IPv6.
    import java.net.Inet4Address;
    import java.net.InetAddress;
    import java.net.NetworkInterface;
    import java.util.ArrayList;
    import java.util.Enumeration;
    import java.util.List;
    
    public class LocalAddresses {
        public static void main(String[] args) throws Exception {
            List<InetAddress> addresses = new ArrayList<>();
            for (Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements(); ) {
                NetworkInterface nif = ifaces.nextElement();
                if (!nif.isUp() || nif.isLoopback() || nif.isVirtual()) continue;
    
                for (Enumeration<InetAddress> addrs = nif.getInetAddresses(); addrs.hasMoreElements(); ) {
                    InetAddress addr = addrs.nextElement();
                    if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) continue; // skip 127.0.0.1, fe80::
                    addresses.add(addr);
                }
            }
    
            // Prefer IPv4 for display
            addresses.stream()
                     .sorted((a, b) -> Boolean.compare(b instanceof Inet4Address, a instanceof Inet4Address))
                     .forEach(a -> System.out.println(a.getHostAddress() + " (" + a.getHostName() + ")"));
        }
    }
    
  3. DNS lookup: resolve a hostname to IPs
    • Useful to get IPs for a remote host or reverse lookup a specific IP.
    import java.net.InetAddress;
    
    public class ResolveHost {
        public static void main(String[] args) throws Exception {
            String host = "example.com"; // replace with your host
            InetAddress[] all = InetAddress.getAllByName(host);
            for (InetAddress inet : all) {
                System.out.println(host + " -> " + inet.getHostAddress());
            }
    
            // Reverse lookup of a specific IP
            InetAddress ip = InetAddress.getByName("203.0.113.10"); // placeholder IP
            System.out.println(ip.getHostAddress() + " reverse -> " + ip.getCanonicalHostName());
        }
    }
    
  4. In a Spring MVC/Jakarta web app
    • Getting the client IP (taking proxies into account) and server info. Utility to extract client IP (checks common proxy headers, then falls back):
    import jakarta.servlet.http.HttpServletRequest;
    import java.util.List;
    
    public class IpUtils {
        private static final List<String> IP_HEADER_CANDIDATES = List.of(
            "X-Forwarded-For",
            "X-Real-IP",
            "CF-Connecting-IP",
            "Fastly-Client-Ip",
            "True-Client-Ip",
            "X-Cluster-Client-Ip",
            "Forwarded",
            "Forwarded-For"
        );
    
        public static String getClientIp(HttpServletRequest request) {
            for (String header : IP_HEADER_CANDIDATES) {
                String value = request.getHeader(header);
                if (value != null && !value.isBlank() && !"unknown".equalsIgnoreCase(value)) {
                    // X-Forwarded-For can contain a list: client, proxy1, proxy2...
                    String first = value.split(",")[0].trim();
                    if (!first.isBlank()) return first;
                }
            }
            return request.getRemoteAddr();
        }
    }
    

Controller example:

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.net.InetAddress;
import java.util.Map;

@RestController
public class NetInfoController {

    @GetMapping("/net-info")
    public Map<String, String> netInfo(HttpServletRequest request) throws Exception {
        String clientIp = IpUtils.getClientIp(request);

        // Server info via servlet and InetAddress
        String serverIp = request.getLocalAddr();     // or request.getServerName()
        String serverHostName = InetAddress.getLocalHost().getHostName();

        return Map.of(
            "clientIp", clientIp,
            "serverIp", serverIp,
            "serverHostName", serverHostName
        );
    }
}

Notes and tips

  • getLocalHost may return 127.0.0.1 if your machine’s hostname isn’t resolvable. Enumerating NetworkInterface is more reliable.
  • For containers/Kubernetes, you may prefer:
    • The interface enumeration approach, or
    • Reading an environment variable like HOSTNAME (if set by the platform).
  • Reverse DNS (getCanonicalHostName) depends on network/DNS config and may be slow; cache if needed.
  • Always handle exceptions: UnknownHostException, SocketException.
  • When behind proxies/load balancers, only trust client-IP headers if your infrastructure sanitizes them; otherwise they can be spoofed.

How to Build a Simple Web Server in Java

Building a simple web server in Java involves creating a server socket to listen on a specific port, accepting client requests, and sending responses back to the client. Below is a basic example of building a simple HTTP server in Java.

Example Code

package org.kodejava.net;

import java.io.*;
import java.net.*;

public class SimpleWebServer {
    public static void main(String[] args) {
        int port = 8080; // Port number the server will listen on

        try (ServerSocket serverSocket = new ServerSocket(port)) {
            System.out.println("Server is listening on port " + port);

            while (true) {
                // Accept incoming client connections
                Socket clientSocket = serverSocket.accept();

                // Create a new thread to handle the request
                new Thread(() -> handleClientRequest(clientSocket)).start();
            }
        } catch (IOException e) {
            System.err.println("Server exception: " + e.getMessage());
            e.printStackTrace();
        }
    }

    private static void handleClientRequest(Socket clientSocket) {
        try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
             PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) {

            // Read the HTTP request from the client
            String requestLine = in.readLine();
            System.out.println("Client request: " + requestLine);

            // Read and discard the rest of the request headers
            while (in.ready() && in.readLine() != null);

            // Build a basic HTTP response
            String responseBody = "<html><body><h1>Welcome to Simple Java Web Server</h1></body></html>";
            String response = "HTTP/1.1 200 OK\r\n" +
                              "Content-Type: text/html\r\n" +
                              "Content-Length: " + responseBody.length() + "\r\n" +
                              "\r\n" +
                              responseBody;

            // Send the HTTP response to the client
            out.write(response);
            out.flush();

        } catch (IOException e) {
            System.err.println("Client handling exception: " + e.getMessage());
            e.printStackTrace();
        } finally {
            try {
                clientSocket.close();
            } catch (IOException e) {
                System.err.println("Failed to close client socket: " + e.getMessage());
            }
        }
    }
}

Steps to Run the Server

  1. Compile the Code
    Save the file as SimpleWebServer.java and compile it:

    javac SimpleWebServer.java
    
  2. Run the Server
    Execute the program:

    java SimpleWebServer
    
  3. Access the Server
    Open a web browser and navigate to http://localhost:808. You should see the message:
    Welcome to Simple Java Web Server.

Key Concepts

  1. ServerSocket:
    The ServerSocket class is used to listen on a specific port for incoming connections.
  2. Socket:
    Represents the client’s connection. You can use the Socket object to read the request and send the response.
  3. HTTP Protocol:
    The server follows a basic structure of HTTP responses:

    • First the status line (e.g., HTTP/1.1 200 OK).
    • Then the headers (e.g., Content-Type and Content-Length).
    • Finally, the response body.
  4. Multithreading:
    Each client connection is handled on a separate thread to allow the server to process multiple requests simultaneously.

Notes

  • Error Handling: Additional error handling should be implemented in production-level servers.
  • Performance: For larger servers, consider using established frameworks like Spring Boot or Jakarta EE.
  • Security: This is a basic example and does not address security concerns like HTTPS, request validation, etc.