All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cp.elements.lang.concurrent.SimpleThreadFactory Maven / Gradle / Ivy

/*
 * Copyright 2011-Present Author or Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.cp.elements.lang.concurrent;

import static java.lang.Thread.UncaughtExceptionHandler;

import java.util.UUID;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.cp.elements.lang.Assert;
import org.cp.elements.lang.IdentifierSequence;
import org.cp.elements.lang.ThrowableUtils;
import org.cp.elements.lang.annotation.NotNull;
import org.cp.elements.lang.annotation.Nullable;
import org.cp.elements.lang.support.UUIDIdentifierSequence;

/**
 * The {@link SimpleThreadFactory} class is a {@link ThreadFactory} implementation that constructs
 * and initializes a new instance of {@link Thread}.
 *
 * @author John Blum
 * @see java.lang.Thread
 * @see java.util.concurrent.ThreadFactory
 * @since 1.0.0
 */
@SuppressWarnings("unused")
public class SimpleThreadFactory implements ThreadFactory {

  private static final Logger logger = Logger.getLogger(SimpleThreadFactory.class.getName());

  protected static final boolean DEFAULT_DAEMON = true;

  protected static final int DEFAULT_PRIORITY = Thread.NORM_PRIORITY;

  protected static final String THREAD_NAME_FORMAT = "%1$s.THREAD-%2$s";

  protected static final ThreadGroup DEFAULT_THREAD_GROUP =
    new ThreadGroup(String.format("%s.THREAD-GROUP", SimpleThreadFactory.class.getName()));

  /**
   * Factory method used to construct a new instance of {@link SimpleThreadFactory} that then can be used to
   * construct and start a new {@link Thread}.
   *
   * @return a new instance of {@link SimpleThreadFactory}.
   * @see org.cp.elements.lang.concurrent.SimpleThreadFactory
   */
  public static @NotNull SimpleThreadFactory newThreadFactory() {
    return new SimpleThreadFactory();
  }

  private Boolean daemon = DEFAULT_DAEMON;

  private ClassLoader contextClassLoader;

  private final IdentifierSequence threadIdGenerator = new UUIDIdentifierSequence();

  private Integer priority;

  private ThreadGroup threadGroup;

  private Thread.UncaughtExceptionHandler uncaughtExceptionHandler;

  /**
   * Generates a unique {@link String identifier} ({@literal ID}) for the new {@link Thread}.
   *
   * @return a {@link String} containing a {@literal unique identifier} (ID) for the new {@link Thread}.
   * @see org.cp.elements.lang.IdentifierSequence
   * @see java.lang.Thread#getId()
   * @see #generateThreadName()
   */
  protected @NotNull String generateThreadId() {
    return this.threadIdGenerator.nextId().toString();
  }

  /**
   * Generates a unique {@link String name} for the new {@link Thread}.
   *
   * @return a {@link String} containing a {@literal unique name} for the new {@link Thread}.
   * @see java.lang.Thread#getName()
   * @see #generateThreadId()
   */
  protected @NotNull String generateThreadName() {
    return String.format(THREAD_NAME_FORMAT, SimpleThreadFactory.class.getName(), generateThreadId());
  }

  /**
   * Constructs a new {@link Thread} initialized with the given, required {@link Runnable} task.
   *
   * @param task {@link Runnable} task to run in a new {@link Thread};  must not be {@literal null}.
   * @return a new {@link Thread} initialized with the given {@link Runnable} task
   * that will be run by the {@link Thread} when started.
   * @see #newThread(String, Runnable)
   * @see #generateThreadName()
   * @see java.lang.Runnable
   * @see java.lang.Thread
   */
  @Override
  public @NotNull Thread newThread(@NotNull Runnable task) {
    return newThread(generateThreadName(), task);
  }

  /**
   * Constructs a new {@link Thread} initialized with the given, required {@link String name} and {@link Runnable} task
   * that will be run by the {@link Thread} when started.
   *
   * @param name {@link String} containing the {@literal name} given to the new {@link Thread};
   * must not be {@literal null}.
   * @param task {@link Runnable} task for the {@link Thread} to execute;
   * must not be {@literal null}.
   * @return a new {@link Thread} created and initialized by {@literal this} {@link ThreadFactory} with the given,
   * required {@link String name} and {@link Runnable} task to execute.
   * @throws IllegalArgumentException if the {@link String name} or the {@link Runnable} task are {@literal null}.
   * @see #getContextClassLoader()
   * @see #isDaemon()
   * @see #getPriority()
   * @see #getThreadGroup()
   * @see #getUncaughtExceptionHandler()
   * @see java.lang.Runnable
   * @see java.lang.Thread
   */
  public @NotNull Thread newThread(@NotNull String name, @NotNull Runnable task) {

    Assert.hasText(name, "Name [%s] is required", name);
    Assert.notNull(task, "Runnable task is required");

    Thread thread = new Thread(getThreadGroup(), task, name);

    thread.setContextClassLoader(getContextClassLoader());
    thread.setDaemon(isDaemon());
    thread.setPriority(getPriority());
    thread.setUncaughtExceptionHandler(getUncaughtExceptionHandler());

    return thread;
  }

  /**
   * Returns the configured {@literal context} {@link ClassLoader} used by new {@link Thread}
   * to resolve {@link Class} types.
   *
   * Returns the calling {@link Thread Thread's} {@link Thread#getContextClassLoader() Thread Context ClassLoader}
   * if not set.
   *
   * @return a reference to the configured context {@link ClassLoader}.
   * @see java.lang.Thread#getContextClassLoader()
   * @see java.lang.ClassLoader
   */
  public @NotNull ClassLoader getContextClassLoader() {

    ClassLoader contextClassLoader = this.contextClassLoader;

    return contextClassLoader != null
      ? contextClassLoader
      : Thread.currentThread().getContextClassLoader();
  }

  /**
   * Determine whether the new {@link Thread} will execute as a {@link Thread#isDaemon() daemon} {@link Thread}.
   *
   * A {@link Thread#isDaemon() daemon} {@link Thread} is a {@link Thread} that will not prevent the JVM
   * from shutting down.
   *
   * @return a boolean value indicating whether the new {@link Thread} will execute as
   * a {@link Thread#isDaemon() daemon} {@link Thread}.
   * @see java.lang.Thread#isDaemon()
   */
  public boolean isDaemon() {
    return Boolean.TRUE.equals(this.daemon);
  }

  /**
   * Returns the configured {@link Thread#getPriority() priority} used by {@literal this} {@link ThreadFactory}
   * when initializing the new {@link Thread}.
   *
   * @return an {@link Integer} value specifying the new {@link Thread Thread's} {@link Thread#getPriority() priority}.
   * @see java.lang.Thread#getPriority()
   */
  public int getPriority() {

    Integer priority = this.priority;

    return priority != null ? priority : DEFAULT_PRIORITY;
  }

  /**
   * Returns the {@link ThreadGroup} to which the new {@link Thread} will be assigned.
   *
   * @return the {@link ThreadGroup} to which the new {@link Thread} will be assigned.
   * @see java.lang.Thread#getThreadGroup()
   * @see java.lang.ThreadGroup
   */
  public @NotNull ThreadGroup getThreadGroup() {

    ThreadGroup threadGroup = this.threadGroup;

    return threadGroup != null ? threadGroup : DEFAULT_THREAD_GROUP;
  }

  /**
   * Returns the {@link Thread.UncaughtExceptionHandler} used for handling any uncaught {@link Exception Exceptions}
   * thrown by the {@link Runnable} tasks during normal execution of the new {@link Thread}.
   *
   * @return a {@link Thread.UncaughtExceptionHandler} used to handle any uncaught {@link Exception Exceptions}
   * thrown by the {@link Runnable} tasks during normal execution of the new {@link Thread}.
   * @see java.lang.Thread.UncaughtExceptionHandler
   */
  public @NotNull UncaughtExceptionHandler getUncaughtExceptionHandler() {

    UncaughtExceptionHandler uncaughtExceptionHandler = this.uncaughtExceptionHandler;

    return uncaughtExceptionHandler != null
      ? uncaughtExceptionHandler
      : SimpleUncaughtExceptionHandler.INSTANCE;
  }

  /**
   * Builder method used to set the new {@link Thread} to execute as a {@link Thread#isDaemon() daemon} {@link Thread}.
   *
   * @return this {@link SimpleThreadFactory}.
   * @see java.lang.Thread#setDaemon(boolean)
   * @see #asUserThread()
   * @see #isDaemon()
   */
  public @NotNull SimpleThreadFactory asDaemonThread() {
    this.daemon = true;
    return this;
  }

  /**
   * Builder method used to set the new {@link Thread} to execute as a {@link Thread#isDaemon() non-daemon},
   * {@literal user} {@link Thread}.
   *
   * @return this {@link SimpleThreadFactory}.
   * @see java.lang.Thread#setDaemon(boolean)
   * @see #asDaemonThread()
   * @see #isDaemon()
   */
  public @NotNull SimpleThreadFactory asUserThread() {
    this.daemon = false;
    return this;
  }

  /**
   * Builder method used to set the {@link Thread.UncaughtExceptionHandler} used by the new {@link Thread}
   * to handle any uncaught {@link Exception Exceptions} throws by the {@link Thread Thread's} {@link Runnable} task.
   *
   * @param uncaughtExceptionHandler {@link Thread.UncaughtExceptionHandler} used by the new {@link Thread}
   * to handle any uncaught {@link Exception Exceptions} thrown by the {@link Thread Thread's} {@link Runnable} task.
   * @return this {@link SimpleThreadFactory}.
   * @see java.lang.Thread#setUncaughtExceptionHandler(UncaughtExceptionHandler)
   * @see java.lang.Thread.UncaughtExceptionHandler
   * @see #getUncaughtExceptionHandler()
   */
  public @NotNull SimpleThreadFactory handleUncaughtExceptionsWith(
      @Nullable UncaughtExceptionHandler uncaughtExceptionHandler) {

    this.uncaughtExceptionHandler = uncaughtExceptionHandler;

    return this;
  }

  /**
   * Builder method used to set the {@link ThreadGroup} in which the new {@link Thread} will be assigned.
   *
   * @param threadGroup {@link ThreadGroup} in which new {@link Thread} will belong.
   * @return this {@link SimpleThreadFactory}.
   * @see java.lang.Thread#Thread(ThreadGroup, Runnable, String)
   * @see java.lang.ThreadGroup
   * @see #getThreadGroup()
   */
  public @NotNull SimpleThreadFactory in(@Nullable ThreadGroup threadGroup) {
    this.threadGroup = threadGroup;
    return this;
  }

  /**
   * Builder method used to set the {@literal context} {@link ClassLoader} used by the new {@link Thread}
   * to resolve {@link Class} types.
   *
   * @param contextClassLoader {@literal context} {@link ClassLoader} used by the new {@link Thread}
   * to resolve {@link Class} types.
   * @return this {@link SimpleThreadFactory}.
   * @see java.lang.Thread#setContextClassLoader(ClassLoader)
   * @see #getContextClassLoader()
   * @see java.lang.ClassLoader
   */
  public @NotNull SimpleThreadFactory resolveTypesWith(@Nullable ClassLoader contextClassLoader) {
    this.contextClassLoader = contextClassLoader;
    return this;
  }

  /**
   * Builder method used to set the {@link Thread#getPriority() priority} of the new {{@link Thread}
   * to {@link Thread#MAX_PRIORITY maximum priority}.
   *
   * @return this {@link SimpleThreadFactory}.
   * @see java.lang.Thread#setPriority(int)
   * @see #withPriority(int)
   * @see #getPriority()
   */
  public @NotNull SimpleThreadFactory withMaxPriority() {
    return withPriority(Thread.MAX_PRIORITY);
  }

  /**
   * Builder method used to set the {@link Thread#getPriority() priority} of the new {{@link Thread}
   * to {@link Thread#MIN_PRIORITY minimum priority}.
   *
   * @return this {@link SimpleThreadFactory}.
   * @see java.lang.Thread#setPriority(int)
   * @see #withPriority(int)
   * @see #getPriority()
   */
  public @NotNull SimpleThreadFactory withMinPriority() {
    return withPriority(Thread.MIN_PRIORITY);
  }

  /**
   * Builder method used to set the {@link Thread#getPriority() priority} of the new {{@link Thread}
   * to {@link Thread#NORM_PRIORITY normal priority}.
   *
   * @return this {@link SimpleThreadFactory}.
   * @see java.lang.Thread#setPriority(int)
   * @see #withPriority(int)
   * @see #getPriority()
   */
  public @NotNull SimpleThreadFactory withNormalPriority() {
    return withPriority(Thread.NORM_PRIORITY);
  }

  /**
   * Builder method used to set the {@link Thread#getPriority() priority} of the new {@link Thread}.
   *
   * @param priority {@link Integer} value specifying the {@link Thread#getPriority() Thread Priority}.
   * @return this {@link SimpleThreadFactory}.
   * @see java.lang.Thread#setPriority(int)
   * @see #getPriority()
   */
  public @NotNull SimpleThreadFactory withPriority(int priority) {
    this.priority = priority;
    return this;
  }

  /**
   * The {@link SimpleUncaughtExceptionHandler} class implements {@link Thread.UncaughtExceptionHandler} by simply
   * {@literal logging} the unhandled error.
   *
   * Use and configure the {@link SimpleThreadFactory} {@link Logger} to adjust logging detail.
   *
   * @see java.lang.Thread.UncaughtExceptionHandler
   */
  protected static class SimpleUncaughtExceptionHandler implements UncaughtExceptionHandler {

    protected static final SimpleUncaughtExceptionHandler INSTANCE = new SimpleUncaughtExceptionHandler();

    /**
     * Returns the {@link Logger} used to log details of the uncaught, unhandled {@link Throwable Exceptions and Errors}
     * thrown by {@link Thread Threads} during execution of the program.
     *
     * @return the {@link Logger} used to log uncaught, unhandled {@link Throwable Exceptions and Errors}.
     * @see java.util.logging.Logger
     */
    protected Logger getLogger() {
      return logger;
    }

    /**
     * Handles any uncaught and unhandled {@link Throwable Exceptions and Errors} thrown from any {@link Thread}
     * of execution.
     *
     * @param thread {@link Thread} from which the uncaught/unhandled {@link Throwable Exception or Error} was thrown.
     * @param cause the uncaught, unhandled {@link Throwable Exception or Error} thrown by the given {@link Thread}.
     * @see java.lang.Thread
     * @see java.lang.Throwable
     * @see #getLogger()
     */
    @Override
    public void uncaughtException(@NotNull Thread thread, @NotNull Throwable cause) {

      Logger logger = getLogger();

      logger.warning(String.format("An unhandled error [%1$s] was thrown by Thread [%2$s] with ID [%3$d]",
        cause.getClass().getName(), thread.getName(), thread.getId()));

      if (logger.isLoggable(Level.FINE)) {
        logger.fine(ThrowableUtils.getStackTrace(cause));
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy