001    /*
002     * Copyright (C) 2010 Google Inc.
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 com.google.common.annotations.Beta;
023    
024    import java.lang.Thread.UncaughtExceptionHandler;
025    import java.util.concurrent.ThreadFactory;
026    import 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     * If no backing thread factory is provided, new threads are created using
040     * {@link Thread#Thread(Runnable)}.
041     *
042     * @author Kurt Alfred Kluever
043     * @since 4
044     */
045    @Beta
046    public 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.
066       * @return this for the builder pattern
067       */
068      public ThreadFactoryBuilder setNameFormat(String nameFormat) {
069        String.format(nameFormat, 0); // fail fast if the format is bad or null
070        this.nameFormat = nameFormat;
071        return this;
072      }
073    
074      /**
075       * Sets daemon or not for new threads created with this ThreadFactory.
076       *
077       * @param daemon whether or not new Threads created with this ThreadFactory
078       *     will be daemon threads
079       * @return this for the builder pattern
080       */
081      public ThreadFactoryBuilder setDaemon(boolean daemon) {
082        this.daemon = daemon;
083        return this;
084      }
085    
086      /**
087       * Sets the priority for new threads created with this ThreadFactory.
088       *
089       * @param priority the priority for new Threads created with this
090       *     ThreadFactory
091       * @return this for the builder pattern
092       */
093      public ThreadFactoryBuilder setPriority(int priority) {
094        // Thread#setPriority() already checks for validity. These error messages
095        // are nicer though and will fail-fast.
096        checkArgument(priority >= Thread.MIN_PRIORITY,
097            "Thread priority (%s) must be >= %s", priority, Thread.MIN_PRIORITY);
098        checkArgument(priority <= Thread.MAX_PRIORITY,
099            "Thread priority (%s) must be <= %s", priority, Thread.MAX_PRIORITY);
100        this.priority = priority;
101        return this;
102      }
103    
104      /**
105       * Sets the {@link UncaughtExceptionHandler} for new threads created with this
106       * ThreadFactory.
107       *
108       * @param uncaughtExceptionHandler the uncaught exception handler for new
109       *     Threads created with this ThreadFactory
110       * @return this for the builder pattern
111       */
112      public ThreadFactoryBuilder setUncaughtExceptionHandler(
113          UncaughtExceptionHandler uncaughtExceptionHandler) {
114        this.uncaughtExceptionHandler = checkNotNull(uncaughtExceptionHandler);
115        return this;
116      }
117    
118      /**
119       * Sets the backing {@link ThreadFactory} for new threads created with this
120       * ThreadFactory. Threads will be created by invoking #newThread(Runnable) on
121       * this backing {@link ThreadFactory}.
122       *
123       * @param backingThreadFactory the backing {@link ThreadFactory} which will
124       *     be delegated to during thread creation.
125       * @return this for the builder pattern
126       *
127       * @see MoreExecutors
128       */
129      public ThreadFactoryBuilder setThreadFactory(
130          ThreadFactory backingThreadFactory) {
131        this.backingThreadFactory = checkNotNull(backingThreadFactory);
132        return this;
133      }
134    
135      /**
136       * Returns a new thread factory using the options supplied during the building
137       * process. After building, it is still possible to change the options used to
138       * build the ThreadFactory and/or build again. State is not shared amongst
139       * built instances.
140       *
141       * @return the fully constructed {@link ThreadFactory}
142       */
143      public ThreadFactory build() {
144        return build(this);
145      }
146    
147      private static ThreadFactory build(ThreadFactoryBuilder builder) {
148        final String nameFormat = builder.nameFormat;
149        final Boolean daemon = builder.daemon;
150        final Integer priority = builder.priority;
151        final UncaughtExceptionHandler uncaughtExceptionHandler =
152            builder.uncaughtExceptionHandler;
153        final ThreadFactory backingThreadFactory = builder.backingThreadFactory;
154        final AtomicLong count = (nameFormat != null) ? new AtomicLong(0) : null;
155        return new ThreadFactory() {
156          @Override public Thread newThread(Runnable runnable) {
157            Thread thread = (backingThreadFactory != null)
158                ? backingThreadFactory.newThread(runnable)
159                : new Thread(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    }