All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.echocat.jomon.spring.ContextLoadThreadGroup Maven / Gradle / Ivy

/*****************************************************************************************
 * *** BEGIN LICENSE BLOCK *****
 *
 * Version: MPL 2.0
 *
 * echocat Jomon, Copyright (c) 2012-2014 echocat
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * *** END LICENSE BLOCK *****
 ****************************************************************************************/

package org.echocat.jomon.spring;

import org.echocat.jomon.runtime.concurrent.StopWatch;
import org.echocat.jomon.runtime.util.Duration;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.*;

import static java.util.Collections.synchronizedMap;
import static java.util.Collections.synchronizedSet;
import static java.util.Collections.unmodifiableSet;
import static java.util.concurrent.TimeUnit.MILLISECONDS;

public class ContextLoadThreadGroup implements ApplicationContextAware, Iterable, UncaughtExceptionHandler {

    private final Map> _failedThreads = synchronizedMap(new HashMap>());
    private final Set _threads = synchronizedSet(new HashSet());

    private String _name;

    public void addAndStart(@Nonnull Thread thread) {
        thread.setUncaughtExceptionHandler(this);
        _threads.add(thread);
        thread.start();
    }

    public void join() throws InterruptedException {
        for (final Thread thread : this) {
            if (thread.isAlive()) {
                thread.join();
            }
        }
        throwRuntimeExceptionOn();
    }

    public boolean join(@Nonnull Duration duration) throws InterruptedException {
        boolean result = true;
        final Iterator i = iterator();
        final StopWatch stopWatch = new StopWatch();
        while (result && i.hasNext()) {
            final Thread thread = i.next();
            if (thread.isAlive()) {
                thread.join(duration.minus(stopWatch.getCurrentDuration()).in(MILLISECONDS));
            }
            result = !thread.isAlive();
        }
        if (result) {
            throwRuntimeExceptionOn();
        }
        return result;
    }

    public void throwExceptionOn() throws Exception {
        throwOn(Exception.class);
    }

    public void throwRuntimeExceptionOn() throws RuntimeException {
        throwOn(RuntimeException.class);
    }

    public  void throwOn(@Nonnull Class allowedThrowableType) throws T {
        synchronized (_failedThreads) {
            final Throwable highestRatedThrowable = findHighestRatedThrowableIn(_failedThreads);
            if (highestRatedThrowable != null) {
                for (final List throwables : _failedThreads.values()) {
                    for (final Throwable throwable : throwables) {
                        if (!highestRatedThrowable.equals(throwable)) {
                            highestRatedThrowable.addSuppressed(throwable);
                        }
                    }
                }
                if (allowedThrowableType.isInstance(highestRatedThrowable)) {
                    throw allowedThrowableType.cast(highestRatedThrowable);
                } else if (highestRatedThrowable instanceof RuntimeException) {
                    throw (RuntimeException) highestRatedThrowable;
                } else if (highestRatedThrowable instanceof Error) {
                    throw (Error) highestRatedThrowable;
                } else {
                    throw new RuntimeException(highestRatedThrowable);
                }
            }
        }
    }

    @Nullable
    protected Throwable findHighestRatedThrowableIn(@Nonnull Map> threadToExceptions) {
        Throwable result = null;
        for (final List exceptions : threadToExceptions.values()) {
            final Throwable current = findHighestRatedThrowableIn(exceptions);
            if (isHigherRated(current, result)) {
                result = current;
            }
        }
        return result;
    }

    @Nullable
    protected Throwable findHighestRatedThrowableIn(@Nonnull Iterable exceptions) {
        Throwable result = null;
        for (final Throwable exception : exceptions) {
            if (isHigherRated(exception, result)) {
                result = exception;
            }
        }
        return result;
    }

    protected boolean isHigherRated(@Nonnull Throwable what, @Nullable Throwable inRelationTo) {
        final boolean result;
        if (inRelationTo instanceof Error) {
            result = false;
        } else if (inRelationTo instanceof Throwable && !(inRelationTo instanceof Exception)) {
            result = what instanceof Error;
        } else if (inRelationTo != null) {
            result = what instanceof Error || (what instanceof Throwable && !(what instanceof Exception));
        } else {
            result = true;
        }
        return result;
    }

    public boolean isAtLeastOneThreadAlive() {
        boolean result = false;
        final Iterator i = iterator();
        while (!result && i.hasNext()) {
            result = i.next().isAlive();
        }
        return result;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (_name == null) {
            _name = applicationContext.getDisplayName();
        }
    }

    @Override
    public Iterator iterator() {
        return getThreads().iterator();
    }

    @Nonnull
    public Set getThreads() {
        final Set threads;
        synchronized (_threads) {
            threads = unmodifiableSet(new HashSet<>(_threads));
        }
        return threads;
    }

    public String getName() {
        return _name;
    }

    public void setName(String name) {
        _name = name;
    }

    @Nonnull
    protected String getNameInternal() {
        final String name = _name;
        return name != null ? name : "ContextLoadThreadGroup";
    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        synchronized (_failedThreads) {
            List throwables = _failedThreads.get(t);
            if (throwables == null) {
                throwables = new ArrayList<>();
                _failedThreads.put(t, throwables);
            }
            throwables.add(e);
        }
    }

    @Override
    public String toString() {
        return getNameInternal();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy