How do I configure a custom thread factory for better debugging?

Configuring a custom thread factory can enhance debugging by customizing the naming and behavior of threads you create for your application. By providing meaningful names to threads and optionally logging their creation, you can significantly simplify debugging and profiling, especially in multi-threaded environments.

Here’s how you can configure a custom thread factory in Java:


Steps to Configure a Custom Thread Factory

  1. Implement a Custom ThreadFactory
    Create a custom class that implements the java.util.concurrent.ThreadFactory interface.

  2. Customize Thread Creation
    Override the newThread() method to provide specific thread naming, priorities, daemon flags, or other settings.

  3. Make the Threads Traceable
    Use meaningful thread names (e.g., include a prefix to indicate the purpose), which can be extremely helpful in logs during debugging.


Example of a Custom Thread Factory

Below is a code example of a custom thread factory:

package org.kodejava.util.concurrent;

import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

public class DebuggableThreadFactory implements ThreadFactory {

   private final String threadNamePrefix;
   private final boolean daemon;
   private final int threadPriority;
   private final AtomicInteger threadCount = new AtomicInteger(1);

   public DebuggableThreadFactory(String threadNamePrefix, boolean daemon, int threadPriority) {
      this.threadNamePrefix = threadNamePrefix != null ? threadNamePrefix : "Thread";
      this.daemon = daemon;
      this.threadPriority = threadPriority;
   }

   @Override
   public Thread newThread(Runnable r) {
      String threadName = threadNamePrefix + "-" + threadCount.getAndIncrement();
      Thread thread = new Thread(r, threadName);
      thread.setDaemon(daemon);
      thread.setPriority(threadPriority);

      // For debugging, log thread creation
      System.out.println("Created thread: " + thread.getName() +
                         ", Daemon: " + daemon +
                         ", Priority: " + thread.getPriority());
      return thread;
   }
}

How to Use the Custom Thread Factory

You can use this custom thread factory to create executor services or individual threads:

Using with an ExecutorService:

package org.kodejava.util.concurrent;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
   public static void main(String[] args) {
      DebuggableThreadFactory threadFactory =
              new DebuggableThreadFactory("Worker", false, Thread.NORM_PRIORITY);

      try (ExecutorService executorService = Executors.newFixedThreadPool(5, threadFactory)) {
         executorService.submit(() -> System.out.println("Task executed by: " + Thread.currentThread().getName()));
         executorService.shutdown();
      }
   }
}

Creating Individual Threads:

package org.kodejava.util.concurrent;

public class Main {
   public static void main(String[] args) {
      DebuggableThreadFactory threadFactory =
              new DebuggableThreadFactory("CustomThread", true, Thread.MAX_PRIORITY);

      Thread customThread = threadFactory.newThread(() -> {
         System.out.println("Running in: " + Thread.currentThread().getName());
      });

      customThread.start();
   }
}

Key Features of the Example

  1. Thread Naming:
    • Threads are named with a prefix and a counter (Worker-1, Worker-2, etc.).
    • Helps identify which thread is handling which task during debugging.
  2. Daemon Threads:
    • You can optionally configure threads as daemon or non-daemon.
    • Daemon threads do not prevent the JVM from exiting.
  3. Thread Priority:
    • You can set thread priorities (e.g., Thread.NORM_PRIORITY, Thread.MAX_PRIORITY, etc.).
  4. Debugging Logs:
    • Logs thread creation for visibility.
  5. Atomic Synchronization:
    • Ensures thread-safe counters when generating unique thread names.

Further Improvements

  • Custom Uncaught Exception Handlers:
    Set an uncaught exception handler for catching unhandled exceptions:

    thread.setUncaughtExceptionHandler((t, e) -> {
      System.err.println("Uncaught exception in thread " + t.getName() + ": " + e.getMessage());
    });
    
  • Thread Context Information:
    Consider associating thread-local variables to store additional debugging details when necessary.

By using this approach, you’ll gain greater control over thread behavior and be better equipped for debugging multi-threaded applications.

What is the peek method in Java Stream API and how to use it?

The peek method in Java’s Stream API is an intermediary operation. This method provides a way to inspect the elements in the stream as they’re being processed by other stream operations. However, it’s important to note that peek should only be used for debugging purposes, as it can be quite disruptive to the stream’s data flow, particularly when it’s used in parallel streams.

The key point of the peek operation is that it’s not a terminal operation (i.e., it doesn’t trigger data processing), instead, it integrates nicely within the operation chain, allowing insights to be gained during the processing phase.

Here’s a simple way of using it with Java:

package org.kodejava.util;

import java.util.stream.Stream;

public class PeekMethodStream {
    public static void main(String[] args) {
        Stream.of(1, 2, 3, 4, 5)
                .peek(i -> System.out.println("Number: " + i))
                .map(i -> i * i)
                .forEach(i -> System.out.println("Squared: " + i));
    }
}

Output:

Number: 1
Squared: 1
Number: 2
Squared: 4
Number: 3
Squared: 9
Number: 4
Squared: 16
Number: 5
Squared: 25

In this code snippet:

  • We create a stream with Stream.of(1, 2, 3, 4, 5).
  • Then we use peek to print each element in its current state: “Number: 1”, “Number: 2”, etc.
  • After that, we use map to square each element.
  • Finally, we use forEach to print the squared numbers: “Squared: 1”, “Squared: 4”, etc.

Remember, use peek carefully and preferably only for debugging purposes.

How do I debug application that uses JavaMail APIs?

To ask Java mail to print out a debug message when you run a Java mail program you can add the mail.debug and set the value to true into the properties used to create the session object.

The other way is to set the debug setting of the session using the session.setDebug() method and pass true as the parameter. This will also turn on the debug message in your Java mail program.

package org.kodejava.mail;

import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Date;
import java.util.Properties;

public class MailDebugDemo {
    public static void main(String[] args) {
        MailDebugDemo.sendMail("[email protected]", "[email protected]",
                "Debug Demo", "Mail Debug Demo");
    }

    private static void sendMail(String mailFrom, String mailTo, String mailSubject,
                                 String mailText) {
        Properties props = new Properties() {{
            put("mail.smtp.auth", "true");
            put("mail.smtp.host", "smtp.gmail.com");
            put("mail.transport.protocol", "smtp");
            put("mail.smtp.port", "587");
            put("mail.smtp.starttls.enable", "true");
            put("mail.smtp.ssl.protocols", "TLSv1.2");
            put("mail.debug", "true");
        }};

        // Creates a mail session. We need to supply username and
        // password for GMail authentication.
        Session session = Session.getInstance(props, new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication("[email protected]", "password");
            }
        });

        try {
            // Creates email message
            Message message = new MimeMessage(session);
            message.setSentDate(new Date());
            message.setFrom(new InternetAddress(mailFrom));
            message.setRecipient(Message.RecipientType.TO,
                    new InternetAddress(mailTo));
            message.setSubject(mailSubject);
            message.setText(mailText);

            // Send a message
            Transport.send(message);
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }
}

Here is an example of debug information produce by the JavaMail API when sending an email using Gmail SMTP server.

DEBUG: JavaMail version 1.5.6
DEBUG: successfully loaded resource: /META-INF/javamail.default.providers
DEBUG: Tables of loaded providers
DEBUG: Providers Listed By Class Name: {com.sun.mail.smtp.SMTPSSLTransport=javax.mail.Provider[TRANSPORT,smtps,com.sun.mail.smtp.SMTPSSLTransport,Oracle], com.sun.mail.smtp.SMTPTransport=javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle], com.sun.mail.imap.IMAPSSLStore=javax.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Oracle], com.sun.mail.pop3.POP3SSLStore=javax.mail.Provider[STORE,pop3s,com.sun.mail.pop3.POP3SSLStore,Oracle], com.sun.mail.imap.IMAPStore=javax.mail.Provider[STORE,imap,com.sun.mail.imap.IMAPStore,Oracle], com.sun.mail.pop3.POP3Store=javax.mail.Provider[STORE,pop3,com.sun.mail.pop3.POP3Store,Oracle]}
DEBUG: Providers Listed By Protocol: {imaps=javax.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Oracle], imap=javax.mail.Provider[STORE,imap,com.sun.mail.imap.IMAPStore,Oracle], smtps=javax.mail.Provider[TRANSPORT,smtps,com.sun.mail.smtp.SMTPSSLTransport,Oracle], pop3=javax.mail.Provider[STORE,pop3,com.sun.mail.pop3.POP3Store,Oracle], pop3s=javax.mail.Provider[STORE,pop3s,com.sun.mail.pop3.POP3SSLStore,Oracle], smtp=javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]}
DEBUG: successfully loaded resource: /META-INF/javamail.default.address.map
DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]
DEBUG SMTP: useEhlo true, useAuth true
DEBUG SMTP: useEhlo true, useAuth true
DEBUG SMTP: trying to connect to host "smtp.gmail.com", port 587, isSSL false
220 smtp.gmail.com ESMTP b2sm4488826pgh.33 - gsmtp
DEBUG SMTP: connected to host "smtp.gmail.com", port: 587
...
...

Maven Dependencies

<dependencies>
    <dependency>
        <groupId>javax.mail</groupId>
        <artifactId>javax.mail-api</artifactId>
        <version>1.5.6</version>
    </dependency>
    <dependency>
        <groupId>javax.mail</groupId>
        <artifactId>mail</artifactId>
        <version>1.4.7</version>
    </dependency>
</dependencies>

Maven Central Maven Central