/** * * Copyright (C) 2013-2015 Emmanuel Keller / Jaeksoft * * http://www.open-search-server.com * * This file is part of OpenSearchServer. * * OpenSearchServer is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenSearchServer is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenSearchServer. * If not, see <http://www.gnu.org/licenses/>. **/ package com.jaeksoft.searchlib.util; import java.lang.Thread.State; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import javax.naming.NamingException; import com.jaeksoft.searchlib.Logging; import com.jaeksoft.searchlib.SearchLibException; public class ThreadUtils { public static class ThreadGroupFactory implements ThreadFactory { private final ThreadGroup group; public ThreadGroupFactory(ThreadGroup group) { this.group = group; } @Override public Thread newThread(Runnable target) { return new Thread(group, target); } } public static class ThreadInfo { private final String name; private final String location; private final State state; private final String fullStackTrace; public ThreadInfo(Thread thread) { this.name = thread.getName(); StackTraceElement[] elements = thread.getStackTrace(); String l = ExceptionUtils.getLocation(elements); if (l == null) l = ExceptionUtils.getFirstLocation(elements); this.fullStackTrace = ExceptionUtils.getFullStackTrace(elements); this.location = l; this.state = thread.getState(); } public String getName() { return name; } public String getLocation() { return location; } public State getState() { return state; } public String getFullStackTrace() { return fullStackTrace; } } public static Thread[] getThreadArray(ThreadGroup group) { Thread[] threads = new Thread[group.activeCount()]; for (;;) { int l = group.enumerate(threads); if (l == threads.length) break; threads = new Thread[l]; } return threads; } public static List<ThreadInfo> getInfos(ThreadGroup... groups) throws SearchLibException, NamingException { if (groups == null) return null; int count = 0; List<Thread[]> threadsArrayList = new ArrayList<Thread[]>(groups.length); for (ThreadGroup group : groups) { Thread[] threadArray = ThreadUtils.getThreadArray(group); threadsArrayList.add(threadArray); count += threadArray.length; } List<ThreadInfo> threadList = new ArrayList<ThreadInfo>(count); for (Thread[] threadArray : threadsArrayList) for (Thread thread : threadArray) threadList.add(new ThreadInfo(thread)); return threadList; } public final static void sleepMs(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { Logging.warn(e); } } public static interface WaitInterface { boolean done(); boolean abort(); } public static boolean waitUntil(long secTimeOut, WaitInterface waiter) { long finalTime = System.currentTimeMillis() + secTimeOut * 1000; while (!waiter.done()) { if (waiter.abort()) return false; if (secTimeOut != 0) if (System.currentTimeMillis() > finalTime) return false; sleepMs(200); } return true; } public static class RecursiveTracker { private int count; private int max; private final int limit; public RecursiveTracker(int limit) { this.count = 0; this.max = 0; this.limit = limit; } public int getCount() { return count; } public RecursiveEntry enter() { return count == limit ? null : new RecursiveEntry(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("limit: "); sb.append(limit); sb.append(" count: "); sb.append(count); sb.append(" max: "); sb.append(max); return sb.toString(); } public class RecursiveEntry { private RecursiveEntry() { count++; if (max < count) max = count; } public void release() { count--; } } } public static abstract class ExceptionCatchThread implements Callable<Exception> { protected Exception exception; public ExceptionCatchThread() { this.exception = null; } public abstract void runner() throws Exception; @Override final public Exception call() { try { runner(); return null; } catch (Exception e) { exception = e; return exception; } } } public static void invokeAndJoin(ExecutorService executor, Collection<? extends ExceptionCatchThread> exceptionThreads) throws SearchLibException { try { executor.invokeAll(exceptionThreads); checkException(exceptionThreads); } catch (Exception e) { ExceptionUtils.<SearchLibException> throwException(e, SearchLibException.class); } } public static void checkException(Collection<? extends ExceptionCatchThread> exceptionThreads) throws Exception { if (exceptionThreads == null) return; Exception exception = null; for (ExceptionCatchThread thread : exceptionThreads) if (exception == null && thread.exception != null) exception = thread.exception; if (exception != null) throw exception; } public static <T> void done(List<Future<T>> futures) throws ExecutionException { ExecutionException exception = null; for (Future<?> future : futures) { try { future.get(); } catch (ExecutionException e) { if (exception == null) exception = e; Logging.warn(e); } catch (InterruptedException e) { Logging.warn(e); } } if (exception != null) throw exception; } }