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    
017    package com.google.common.util.concurrent;
018    
019    import static com.google.common.base.Preconditions.checkArgument;
020    import static com.google.common.base.Preconditions.checkNotNull;
021    
022    import java.lang.Thread.UncaughtExceptionHandler;
023    import java.util.concurrent.Executors;
024    import java.util.concurrent.ThreadFactory;
025    import java.util.concurrent.atomic.AtomicLong;
026    
027    /**
028     * A ThreadFactory builder, providing any combination of these features:
029     * <ul>
030     * <li> whether threads should be marked as {@linkplain Thread#setDaemon daemon}
031     * threads
032     * <li> a {@linkplain ThreadFactoryBuilder#setNameFormat naming format}
033     * <li> a {@linkplain Thread#setPriority thread priority}
034     * <li> an {@linkplain Thread#setUncaughtExceptionHandler uncaught exception
035     * handler}
036     * <li> a {@linkplain ThreadFactory#newThread backing thread factory}
037     * </ul>
038     * If no backing thread factory is provided, a default backing thread factory is
039     * used as if by calling {@code setThreadFactory(}{@link
040     * Executors#defaultThreadFactory()}{@code )}.
041     *
042     * @author Kurt Alfred Kluever
043     * @since 4.0
044     */
045    public final class ThreadFactoryBuilder {
046      private String nameFormat = null;
047      private Boolean daemon = null;
048      private Integer priority = null;
049      private UncaughtExceptionHandler uncaughtExceptionHandler = null;
050      private ThreadFactory backingThreadFactory = null;
051    
052      /**
053       * Creates a new {@link ThreadFactory} builder.
054       */
055      public ThreadFactoryBuilder() {}
056    
057      /**
058       * Sets the naming format to use when naming threads ({@link Thread#setName})
059       * which are created with this ThreadFactory.
060       *
061       * @param nameFormat a {@link String#format(String, Object...)}-compatible
062       *     format String, to which a unique integer (0, 1, etc.) will be supplied
063       *     as the single parameter. This integer will be unique to the built
064       *     instance of the ThreadFactory and will be assigned sequentially.
065       * @return this for the builder pattern
066       */
067      public ThreadFactoryBuilder setNameFormat(String nameFormat) {
068        String.format(nameFormat, 0); // fail fast if the format is bad or null
069        this.nameFormat = nameFormat;
070        return this;
071      }
072    
073      /**
074       * Sets daemon or not for new threads created with this ThreadFactory.
075       *
076       * @param daemon whether or not new Threads created with this ThreadFactory
077       *     will be daemon threads
078       * @return this for the builder pattern
079       */
080      public ThreadFactoryBuilder setDaemon(boolean daemon) {
081        this.daemon = daemon;
082        return this;
083      }
084    
085      /**
086       * Sets the priority for new threads created with this ThreadFactory.
087       *
088       * @param priority the priority for new Threads created with this
089       *     ThreadFactory
090       * @return this for the builder pattern
091       */
092      public ThreadFactoryBuilder setPriority(int priority) {
093        // Thread#setPriority() already checks for validity. These error messages
094        // are nicer though and will fail-fast.
095        checkArgument(priority >= Thread.MIN_PRIORITY,
096            "Thread priority (%s) must be >= %s", priority, Thread.MIN_PRIORITY);
097        checkArgument(priority <= Thread.MAX_PRIORITY,
098            "Thread priority (%s) must be <= %s", priority, Thread.MAX_PRIORITY);
099        this.priority = priority;
100        return this;
101      }
102    
103      /**
104       * Sets the {@link UncaughtExceptionHandler} for new threads created with this
105       * ThreadFactory.
106       *
107       * @param uncaughtExceptionHandler the uncaught exception handler for new
108       *     Threads created with this ThreadFactory
109       * @return this for the builder pattern
110       */
111      public ThreadFactoryBuilder setUncaughtExceptionHandler(
112          UncaughtExceptionHandler uncaughtExceptionHandler) {
113        this.uncaughtExceptionHandler = checkNotNull(uncaughtExceptionHandler);
114        return this;
115      }
116    
117      /**
118       * Sets the backing {@link ThreadFactory} for new threads created with this
119       * ThreadFactory. Threads will be created by invoking #newThread(Runnable) on
120       * this backing {@link ThreadFactory}.
121       *
122       * @param backingThreadFactory the backing {@link ThreadFactory} which will
123       *     be delegated to during thread creation.
124       * @return this for the builder pattern
125       *
126       * @see MoreExecutors
127       */
128      public ThreadFactoryBuilder setThreadFactory(
129          ThreadFactory backingThreadFactory) {
130        this.backingThreadFactory = checkNotNull(backingThreadFactory);
131        return this;
132      }
133    
134      /**
135       * Returns a new thread factory using the options supplied during the building
136       * process. After building, it is still possible to change the options used to
137       * build the ThreadFactory and/or build again. State is not shared amongst
138       * built instances.
139       *
140       * @return the fully constructed {@link ThreadFactory}
141       */
142      public ThreadFactory build() {
143        return build(this);
144      }
145    
146      private static ThreadFactory build(ThreadFactoryBuilder builder) {
147        final String nameFormat = builder.nameFormat;
148        final Boolean daemon = builder.daemon;
149        final Integer priority = builder.priority;
150        final UncaughtExceptionHandler uncaughtExceptionHandler =
151            builder.uncaughtExceptionHandler;
152        final ThreadFactory backingThreadFactory =
153            (builder.backingThreadFactory != null)
154            ? builder.backingThreadFactory
155            : Executors.defaultThreadFactory();
156        final AtomicLong count = (nameFormat != null) ? new AtomicLong(0) : null;
157        return new ThreadFactory() {
158          @Override public Thread newThread(Runnable runnable) {
159            Thread thread = backingThreadFactory.newThread(runnable);
160            if (nameFormat != null) {
161              thread.setName(String.format(nameFormat, count.getAndIncrement()));
162            }
163            if (daemon != null) {
164              thread.setDaemon(daemon);
165            }
166            if (priority != null) {
167              thread.setPriority(priority);
168            }
169            if (uncaughtExceptionHandler != null) {
170              thread.setUncaughtExceptionHandler(uncaughtExceptionHandler);
171            }
172            return thread;
173          }
174        };
175      }
176    }