001/*
002 * Copyright (C) 2010 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.util.concurrent;
018
019import static com.google.common.base.Preconditions.checkArgument;
020import static com.google.common.base.Preconditions.checkNotNull;
021
022import java.lang.Thread.UncaughtExceptionHandler;
023import java.util.Locale;
024import java.util.concurrent.Executors;
025import java.util.concurrent.ThreadFactory;
026import java.util.concurrent.atomic.AtomicLong;
027
028/**
029 * A ThreadFactory builder, providing any combination of these features:
030 * <ul>
031 * <li> whether threads should be marked as {@linkplain Thread#setDaemon daemon}
032 * threads
033 * <li> a {@linkplain ThreadFactoryBuilder#setNameFormat naming format}
034 * <li> a {@linkplain Thread#setPriority thread priority}
035 * <li> an {@linkplain Thread#setUncaughtExceptionHandler uncaught exception
036 * handler}
037 * <li> a {@linkplain ThreadFactory#newThread backing thread factory}
038 * </ul>
039 * <p>If no backing thread factory is provided, a default backing thread factory is
040 * used as if by calling {@code setThreadFactory(}{@link
041 * Executors#defaultThreadFactory()}{@code )}.
042 *
043 * @author Kurt Alfred Kluever
044 * @since 4.0
045 */
046public final class ThreadFactoryBuilder {
047  private String nameFormat = null;
048  private Boolean daemon = null;
049  private Integer priority = null;
050  private UncaughtExceptionHandler uncaughtExceptionHandler = null;
051  private ThreadFactory backingThreadFactory = null;
052
053  /**
054   * Creates a new {@link ThreadFactory} builder.
055   */
056  public ThreadFactoryBuilder() {}
057
058  /**
059   * Sets the naming format to use when naming threads ({@link Thread#setName})
060   * which are created with this ThreadFactory.
061   *
062   * @param nameFormat a {@link String#format(String, Object...)}-compatible
063   *     format String, to which a unique integer (0, 1, etc.) will be supplied
064   *     as the single parameter. This integer will be unique to the built
065   *     instance of the ThreadFactory and will be assigned sequentially. For
066   *     example, {@code "rpc-pool-%d"} will generate thread names like
067   *     {@code "rpc-pool-0"}, {@code "rpc-pool-1"}, {@code "rpc-pool-2"}, etc.
068   * @return this for the builder pattern
069   */
070  @SuppressWarnings("ReturnValueIgnored")
071  public ThreadFactoryBuilder setNameFormat(String nameFormat) {
072    format(nameFormat, 0); // fail fast if the format is bad or null
073    this.nameFormat = nameFormat;
074    return this;
075  }
076
077  /**
078   * Sets daemon or not for new threads created with this ThreadFactory.
079   *
080   * @param daemon whether or not new Threads created with this ThreadFactory
081   *     will be daemon threads
082   * @return this for the builder pattern
083   */
084  public ThreadFactoryBuilder setDaemon(boolean daemon) {
085    this.daemon = daemon;
086    return this;
087  }
088
089  /**
090   * Sets the priority for new threads created with this ThreadFactory.
091   *
092   * @param priority the priority for new Threads created with this
093   *     ThreadFactory
094   * @return this for the builder pattern
095   */
096  public ThreadFactoryBuilder setPriority(int priority) {
097    // Thread#setPriority() already checks for validity. These error messages
098    // are nicer though and will fail-fast.
099    checkArgument(priority >= Thread.MIN_PRIORITY,
100        "Thread priority (%s) must be >= %s", priority, Thread.MIN_PRIORITY);
101    checkArgument(priority <= Thread.MAX_PRIORITY,
102        "Thread priority (%s) must be <= %s", priority, Thread.MAX_PRIORITY);
103    this.priority = priority;
104    return this;
105  }
106
107  /**
108   * Sets the {@link UncaughtExceptionHandler} for new threads created with this
109   * ThreadFactory.
110   *
111   * @param uncaughtExceptionHandler the uncaught exception handler for new
112   *     Threads created with this ThreadFactory
113   * @return this for the builder pattern
114   */
115  public ThreadFactoryBuilder setUncaughtExceptionHandler(
116      UncaughtExceptionHandler uncaughtExceptionHandler) {
117    this.uncaughtExceptionHandler = checkNotNull(uncaughtExceptionHandler);
118    return this;
119  }
120
121  /**
122   * Sets the backing {@link ThreadFactory} for new threads created with this
123   * ThreadFactory. Threads will be created by invoking #newThread(Runnable) on
124   * this backing {@link ThreadFactory}.
125   *
126   * @param backingThreadFactory the backing {@link ThreadFactory} which will
127   *     be delegated to during thread creation.
128   * @return this for the builder pattern
129   *
130   * @see MoreExecutors
131   */
132  public ThreadFactoryBuilder setThreadFactory(
133      ThreadFactory backingThreadFactory) {
134    this.backingThreadFactory = checkNotNull(backingThreadFactory);
135    return this;
136  }
137
138  /**
139   * Returns a new thread factory using the options supplied during the building
140   * process. After building, it is still possible to change the options used to
141   * build the ThreadFactory and/or build again. State is not shared amongst
142   * built instances.
143   *
144   * @return the fully constructed {@link ThreadFactory}
145   */
146  public ThreadFactory build() {
147    return build(this);
148  }
149
150  private static ThreadFactory build(ThreadFactoryBuilder builder) {
151    final String nameFormat = builder.nameFormat;
152    final Boolean daemon = builder.daemon;
153    final Integer priority = builder.priority;
154    final UncaughtExceptionHandler uncaughtExceptionHandler =
155        builder.uncaughtExceptionHandler;
156    final ThreadFactory backingThreadFactory =
157        (builder.backingThreadFactory != null)
158        ? builder.backingThreadFactory
159        : Executors.defaultThreadFactory();
160    final AtomicLong count = (nameFormat != null) ? new AtomicLong(0) : null;
161    return new ThreadFactory() {
162      @Override public Thread newThread(Runnable runnable) {
163        Thread thread = backingThreadFactory.newThread(runnable);
164        if (nameFormat != null) {
165          thread.setName(format(nameFormat, count.getAndIncrement()));
166        }
167        if (daemon != null) {
168          thread.setDaemon(daemon);
169        }
170        if (priority != null) {
171          thread.setPriority(priority);
172        }
173        if (uncaughtExceptionHandler != null) {
174          thread.setUncaughtExceptionHandler(uncaughtExceptionHandler);
175        }
176        return thread;
177      }
178    };
179  }
180
181  private static String format(String format, Object... args) {
182    return String.format(Locale.ROOT, format, args);
183  }
184}