package io.airlift.concurrent;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.airlift.log.Logger;

import java.util.concurrent.ThreadFactory;

public final class Threads
{
    private static final Logger log = Logger.get(Threads.class);

    private Threads() {}

    /**
     * Creates a {@link ThreadFactory} that creates named threads
     * using the specified naming format.
     *
     * @param nameFormat a {@link String#format(String, Object...)}-compatible
     * format string, to which a string will be supplied as the single
     * parameter. This string will be unique to this instance of the
     * ThreadFactory and will be assigned sequentially.
     * @return the created ThreadFactory
     */
    public static ThreadFactory threadsNamed(String nameFormat)
    {
        GroupedThreadFactory delegate = new GroupedThreadFactory(threadGroupName(nameFormat));
        return new ThreadFactoryBuilder()
                .setNameFormat(nameFormat)
                .setThreadFactory(new ContextClassLoaderThreadFactory(Thread.currentThread().getContextClassLoader(), delegate))
                .build();
    }

    /**
     * Creates a {@link ThreadFactory} that creates named daemon threads.
     * using the specified naming format.
     *
     * @param nameFormat see {@link #threadsNamed(String)}
     * @return the created ThreadFactory
     */
    public static ThreadFactory daemonThreadsNamed(String nameFormat)
    {
        GroupedThreadFactory delegate = new GroupedThreadFactory(threadGroupName(nameFormat));
        return new ThreadFactoryBuilder()
                .setNameFormat(nameFormat)
                .setDaemon(true)
                .setThreadFactory(new ContextClassLoaderThreadFactory(Thread.currentThread().getContextClassLoader(), delegate))
                .build();
    }

    private static String threadGroupName(String nameFormat)
    {
        String groupFormat = nameFormat.replace("%d", "%s");
        if (!nameFormat.equals(groupFormat)) {
            log.warn("Invalid thread group nameFormat: %s", nameFormat);
        }
        return String.format(groupFormat, "group");
    }

    private static class ContextClassLoaderThreadFactory
            implements ThreadFactory
    {
        private final ClassLoader classLoader;
        private final ThreadFactory delegate;

        public ContextClassLoaderThreadFactory(ClassLoader classLoader, ThreadFactory delegate)
        {
            this.classLoader = classLoader;
            this.delegate = delegate;
        }

        @Override
        public Thread newThread(Runnable runnable)
        {
            Thread thread = delegate.newThread(runnable);
            thread.setContextClassLoader(classLoader);
            return thread;
        }
    }

    private static final class GroupedThreadFactory
            implements ThreadFactory
    {
        private final ThreadGroup threadGroup;

        public GroupedThreadFactory(String name)
        {
            this(new ThreadGroup(name));
        }

        public GroupedThreadFactory(ThreadGroup threadGroup)
        {
            this.threadGroup = threadGroup;
        }

        @Override
        public Thread newThread(Runnable runnable)
        {
            return new Thread(threadGroup, runnable);
        }
    }
}

@@ -2997,17 +2997,32 @@
     this
-(
+.threadGroup =  
 new Thre
@@ -3034,17 +3034,16 @@
 up(name)
-)
 ;%0A      
@@ -3059,194 +3059,262 @@
     
-public GroupedThreadFactory(ThreadGroup threadGroup)%0A        %7B%0A            this.threadGroup = threadGroup;%0A        %7D%0A%0A        @Override%0A        public Thread newThread(Runnable runnable)
+@Override%0A        public Thread newThread(Runnable runnable)%0A        %7B%0A            return new Thread(threadGroup, runnable);%0A        %7D%0A%0A        @Override%0A        protected void finalize()%0A        %7B%0A            try %7B%0A                threadGroup.destroy();
 %0A   
@@ -3310,33 +3310,37 @@
 troy();%0A        
-%7B
+    %7D
 %0A            ret
@@ -3340,49 +3340,130 @@
     
-return new Thread(
+catch (RuntimeException e) %7B%0A                log.warn(e, %22Leaked 
 thread
-G
+ g
 roup
-, runnable);
+ '%25s'%22, threadGroup.getName());%0A            %7D
 %0A   

Add finalizer to cleanup ThreadGroup