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

org.eclipse.jetty.util.ExceptionUtil Maven / Gradle / Ivy

The newest version!
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.util;

import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;

import org.eclipse.jetty.util.thread.Invocable;

/**
 * 

Exception (or rather {@link Throwable} utility methods.

*/ public class ExceptionUtil { private static final int MAX_SUPPRESSED = 10; /** *

Convert a {@link Throwable} to a specific type by casting or construction on a new instance.

* * @param The type of the {@link Throwable} to be returned * @param type The type of the Throwable to be thrown. It must have a constructor that * takes a {@link Throwable} as a cause. * @param throwable The {@link Throwable} or null * @return A {@link Throwable} of type T or null. * @throws IllegalArgumentException if the passed type cannot be constructed with a cause. */ public static T as(Class type, Throwable throwable) throws IllegalArgumentException { if (throwable == null) return null; if (type.isInstance(throwable)) { @SuppressWarnings("unchecked") T t = (T)throwable; return t; } try { Constructor constructor = type.getConstructor(Throwable.class); return constructor.newInstance(throwable); } catch (Exception e) { IllegalArgumentException iae = new IllegalArgumentException(e); if (areNotAssociated(iae, throwable)) iae.addSuppressed(throwable); throw iae; } catch (Throwable t) { if (areNotAssociated(t, throwable)) t.addSuppressed(throwable); throw t; } } /** * Throw a {@link Throwable} as a checked {@link Exception} if it * cannot be thrown as unchecked. * @param throwable The {@link Throwable} to throw or null. * @throws Error If the passed {@link Throwable} is an {@link Error}. * @throws Exception Otherwise, if the passed {@link Throwable} is not null. */ public static void ifExceptionThrow(Throwable throwable) throws Error, Exception { if (throwable == null) return; if (throwable instanceof Error error) throw error; if (throwable instanceof Exception exception) throw exception; throw new RuntimeException(throwable); } /** * Throw a {@link Throwable} as an unchecked {@link Exception}. * @param throwable The {@link Throwable} to throw or null. * @throws Error If the passed {@link Throwable} is an {@link Error}. * @throws RuntimeException Otherwise, if the passed {@link Throwable} is not null. */ public static void ifExceptionThrowUnchecked(Throwable throwable) throws Error, RuntimeException { if (throwable == null) return; if (throwable instanceof RuntimeException runtimeException) throw runtimeException; if (throwable instanceof Error error) throw error; throw new RuntimeException(throwable); } /** *

Throw a {@link Throwable} as a specific type, casting or construction as required.

* * @param The type of the {@link Throwable} to be thrown if throwable is not null. * @param type The type of the Throwable to be thrown. It must have a constructor that * takes a {@link Throwable} as a cause. * @param throwable A {@link Throwable} or null. * @throws Error If the passed {@link Throwable} is an {@link Error}. * @throws RuntimeException If the passed {@link Throwable} is a {@link RuntimeException}. * @throws T Thrown in throwable is not null and neither an {@link Error} nor {@link RuntimeException}. * @throws IllegalArgumentException if the passed type cannot be constructed with a cause. */ public static void ifExceptionThrowAs(Class type, Throwable throwable) throws Error, RuntimeException, T, IllegalArgumentException { if (throwable == null) return; if (throwable instanceof Error error) throw error; if (throwable instanceof RuntimeException runtimeException) throw runtimeException; throw as(type, throwable); } /** *

Throw a {@link Throwable} as a specific type, casting or construction as required.

* @param type The type of the Throwable to be thrown. It must have a constructor that * takes a {@link Throwable} as a cause. * @param throwable A {@link Throwable} or null. * @param The type of the {@link Throwable} to be thrown if throwable is not null. * @throws T Thrown in throwable is not null and neither an {@link Error} nor {@link RuntimeException}. */ public static void ifExceptionThrowAllAs(Class type, Throwable throwable) throws T { if (throwable == null) return; throw as(type, throwable); } /** Check if two {@link Throwable}s are associated. * @param t1 A Throwable or null * @param t2 Another Throwable or null * @return true iff the exceptions are not associated by being the same instance, sharing a cause or one suppressing the other. */ public static boolean areAssociated(Throwable t1, Throwable t2) { return t1 != null && t2 != null && !areNotAssociated(t1, t2); } /** Check if two {@link Throwable}s are associated. * @param t1 A Throwable or null * @param t2 Another Throwable or null * @return true iff the exceptions are not associated by being the same instance, sharing a cause or one suppressing the other. */ public static boolean areNotAssociated(Throwable t1, Throwable t2) { if (t1 == null || t2 == null) return false; while (t1 != null) { Throwable two = t2; while (two != null) { if (t1 == two) return false; if (t1.getCause() == two) return false; if (Arrays.asList(t1.getSuppressed()).contains(two)) return false; if (Arrays.asList(two.getSuppressed()).contains(t1)) return false; two = two.getCause(); } t1 = t1.getCause(); } return true; } /** * Add a suppressed exception if it is not associated. * @see #areNotAssociated(Throwable, Throwable) * @param throwable The main Throwable * @param suppressed The Throwable to suppress if it is not associated. */ public static void addSuppressedIfNotAssociated(Throwable throwable, Throwable suppressed) { if (areNotAssociated(throwable, suppressed)) { int s = throwable.getSuppressed().length; if (s < MAX_SUPPRESSED) throwable.addSuppressed(suppressed); else if (s == MAX_SUPPRESSED) throwable.addSuppressed(new IllegalStateException("Too many suppressed", suppressed)); } } /** * Decorate a Throwable with the suppressed errors and return it. * @param t the throwable * @param errors the list of errors * @return the original throwable with suppressed errors * @param of type Throwable */ public static T withSuppressed(T t, List errors) { if (errors != null) errors.stream().filter(e -> areNotAssociated(t, e)).forEach(t::addSuppressed); return t; } /*

A utility class for combining multiple exceptions which can be * used in the following pattern: *

     *     ExceptionUtil.MultiException multiException = new ExceptionUtil.MultiException();
     *     manyThings.forEach(task ->
     *       {
     *         try
     *         {
     *             task.run();
     *         }
     *         catch(Throwable t)
     *         {
     *             multiException.add(t)
     *         }
     *       }
     *     }
     *     multiException.ifExceptionalThrowAs(IOException::new);
     * 
*

In some cases, an alternative style can be used: *

     *     ExceptionUtil.MultiException multiException = new ExceptionUtil.MultiException();
     *     manyThings.forEach(multiException::callAndCatch);
     *     multiException.ifExceptionalThrow();
     * 
*/ public static class MultiException { private Throwable _multiException; public void add(Throwable t) { _multiException = ExceptionUtil.combine(_multiException, t); } public void ifExceptionThrow() throws Exception { ExceptionUtil.ifExceptionThrow(_multiException); } public void ifExceptionThrowRuntime() { ExceptionUtil.ifExceptionThrowUnchecked(_multiException); } public void ifExceptionThrowAs(Class type) throws T { ExceptionUtil.ifExceptionThrowAs(type, _multiException); } public void callAndCatch(Invocable.Callable task) { ExceptionUtil.call(task, this::add); } } /** *

Combine two, possible null, {@link Throwable}s in a style to facilitate handling * multiple exceptions that are accumulated as suppressed exceptions. This is freqently * used in the following pattern:

*
     *     Throwable multiException = null;
     *     for (Runnable task : manyThings)
     *     {
     *         try
     *         {
     *             task.run();
     *         }
     *         catch(Throwable t)
     *         {
     *             multiException = multiException.combine(multiException, t)
     *         }
     *     }
     *     MultiException.ifExceptionalThrow(multiException);
     * 
* @param t1 A Throwable or null * @param t2 Another Throwable or null * @return t1 with t2 suppressed, or null. */ public static Throwable combine(Throwable t1, Throwable t2) { if (t1 == null) return t2; addSuppressedIfNotAssociated(t1, t2); return t1; } /** Call a method that handles a {@link Throwable}, catching and associating any exception that it throws. * @param cause The {@link Throwable} to pass to the consumer * @param consumer The handler of a {@link Throwable} */ public static void call(Throwable cause, Consumer consumer) { try { consumer.accept(cause); } catch (Throwable t) { ExceptionUtil.addSuppressedIfNotAssociated(t, cause); throw t; } } /** * Call a {@link Invocable.Callable} and handle any resulting failures * @param callable The {@link org.eclipse.jetty.util.thread.Invocable.Callable} to call * @param failure A handler of failures from the call * @see #run(Runnable, Consumer) */ public static void call(Invocable.Callable callable, Consumer failure) { try { callable.call(); } catch (Throwable thrown) { try { failure.accept(thrown); } catch (Throwable alsoThrown) { ExceptionUtil.addSuppressedIfNotAssociated(alsoThrown, thrown); ExceptionUtil.ifExceptionThrowUnchecked(alsoThrown); } } } /** * Call a {@link Runnable} and handle failures * @param runnable The runnable to call * @param failure The handling of failures * @see #call(Throwable, Consumer) */ public static void run(Runnable runnable, Consumer failure) { try { runnable.run(); } catch (Throwable thrown) { try { failure.accept(thrown); } catch (Throwable alsoThrown) { ExceptionUtil.addSuppressedIfNotAssociated(alsoThrown, thrown); ExceptionUtil.ifExceptionThrowUnchecked(alsoThrown); } } } /** * Call a handler of {@link Throwable} and then always call another, suppressing any exceptions thrown. * @param cause The {@link Throwable} to be passed to both consumers. * @param call The first {@link Consumer} of {@link Throwable} to call. * @param then The second {@link Consumer} of {@link Throwable} to call. */ public static void callAndThen(Throwable cause, Consumer call, Consumer then) { try { call.accept(cause); } catch (Throwable t) { addSuppressedIfNotAssociated(cause, t); } finally { then.accept(cause); } } /** * Call a handler of {@link Throwable} and then always call a {@link Runnable}, suppressing any exceptions thrown. * @param cause The {@link Throwable} to be passed to both consumers. * @param call The {@link Consumer} of {@link Throwable} to call. * @param then The {@link Runnable} to call. */ public static void callAndThen(Throwable cause, Consumer call, Runnable then) { try { call.accept(cause); } catch (Throwable t) { addSuppressedIfNotAssociated(cause, t); } finally { then.run(); } } /** * Call a {@link Runnable} and then always call another, ignoring any exceptions thrown. * @param call The first {@link Runnable} to call. * @param then The second {@link Runnable} to call. */ public static void callAndThen(Runnable call, Runnable then) { try { call.run(); } catch (Throwable t) { // ignored } finally { then.run(); } } /** *

Get from a {@link CompletableFuture} and convert any uncheck exceptions to {@link RuntimeException}.

* @param completableFuture The future to get from. * @param The type of the {@code CompletableFuture} * @return The value from calling {@link CompletableFuture#get()} * @throws RuntimeException if the call to {@link CompletableFuture#get()} throws. */ public static T get(CompletableFuture completableFuture) { try { return completableFuture.get(); } catch (InterruptedException e) { throw new RuntimeException(e); } catch (ExecutionException e) { throw new RuntimeException(e.getCause()); } } private ExceptionUtil() { } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy