static void toStream(Serializable obj, OutputStream stream) { try (ObjectOutputStream objectOutput = new ObjectOutputStream(stream)) { objectOutput.writeObject(obj); } catch (IOException e) { throw Errors.asRuntime(e); } }
void pauseForFilesystem() { Errors.rethrow().run(() -> Thread.sleep(FILESYSTEM_RESOLUTION_MS)); }
foodOnPlate.forEach(Errors.suppress().wrap(this::eat)); foodOnPlate.forEach(Errors.log().wrap(this::eat)); foodOnPlate.forEach(Errors.rethrow().wrap(this::eat)); foodOnPlate.forEach(Errors.dialog().wrap(this::eat)); Errors retryHandler = Errors.createHandling(error -> { if (error instanceof Barf) { Food cause = ((Barf) error).looksLikeItUsedToBe(); foodOnPlate.forEach(retryHandler.wrap(this::eat));
@Test public void testWiresCrossed() { DurianPlugins.resetForTesting(); Errors.resetForTesting(); DurianPlugins.register(Errors.Plugins.Log.class, new TestHandler("Log")); DurianPlugins.register(Errors.Plugins.Dialog.class, new TestHandler("Dialog")); try { Errors.suppress().run(ErrorsTest::throwException); try { Errors.rethrow().run(ErrorsTest::throwException); } catch (RuntimeException e) { // it should pass the RuntimeException unphased Assert.assertNull(e.getCause()); } try { Errors.log().run(ErrorsTest::throwException); } catch (RuntimeException e) { Assert.assertEquals("Log", e.getMessage()); } try { Errors.dialog().run(ErrorsTest::throwException); } catch (RuntimeException e) { Assert.assertEquals("Dialog", e.getMessage()); } } finally { DurianPlugins.resetForTesting(); Errors.resetForTesting(); } }
@Override public void finalize() { // If we're taking the "default value" route, Errors has you covered Food logged = Errors.log().getWithDefault(() -> cook("spaghetti"), CEREAL); // log to twitter Food suppressed = Errors.suppress().getWithDefault(() -> cook("spaghetti"), CEREAL); // suppress to insecurity buffer // If we're taking the "rethrow RuntimeException" route, then specifying a // default value would be nonsensical, so we don't do it Food rethrow = Errors.rethrow().get(() -> cook("spaghetti")); // I'm stressed, don't judge me Errors.suppress().run(() -> { eat(logged); eat(suppressed); eat(rethrow); }); }
@SuppressWarnings("unused") public void wrapping() { // If your functional interface has 0 or 1 outputs, and 0 or 1 inputs, then Errors can wrap it into its standard Java 8 form Throwing.Runnable marathon = () -> { throw new IAmOnFire(); }; java.lang.Runnable marathonSafe = Errors.log().wrap(marathon); Throwing.Consumer<Food> eat = this::eat; java.util.function.Consumer<Food> eatSafe = Errors.log().wrap(eat); Throwing.Supplier<Food> cookSpatula = () -> cook("spatula"); java.util.function.Supplier<Food> cookSpatulaOrGetCereal = Errors.log().wrapWithDefault(cookSpatula, CEREAL); java.util.function.Supplier<Food> cookSpatulaOrBurn = Errors.rethrow().wrap(cookSpatula); Throwing.Function<String, Food> cookAnything = this::cook; java.util.function.Function<String, Food> cookAnythingOrGetCereal = Errors.log().wrapWithDefault(this::cook, CEREAL); java.util.function.Function<String, Food> cookAnythingOrBurn = Errors.rethrow().wrap(this::cook); // If your function has more than 1 input, you can either // A) Make a wrapper function that calls Errors.get() to return a value (recommended) // B) Make a "wrapper wrapper" (see https://github.com/diffplug/durian/blob/master/test/com/diffplug/common/base/ErrorsMultipleInputs.png) // If your function has more than 1 output, see https://github.com/diffplug/durian/blob/master/test/com/diffplug/common/base/ErrorsMultipleOutputs.png }
/** * Opens a dialog to notify the user of any exceptions. It should be used in cases where * an error is too severe to be silently logged. * <p> * By default, dialog() opens a JOptionPane. To modify this behavior in your application, * call DurianPlugins.set(Errors.Plugins.Dialog.class, error -> openMyDialog(error)); * <p> * For a non-interactive console application, a good implementation of would probably * print the error and call System.exit(). * * @see DurianPlugins * @see Errors.Plugins.OnErrorThrowAssertion */ @SuppressFBWarnings(value = "LI_LAZY_INIT_STATIC", justification = "This race condition is fine, as explained in the comment below.") public static Handling dialog() { if (dialog == null) { // There is an acceptable race condition here. See Errors.log() for details. dialog = createHandling(DurianPlugins.get(Plugins.Dialog.class, Plugins::defaultDialog)); } return dialog; }
/** * Retries an action every ms, for 250ms, until it finally works or fails. * * Makes FS operations more reliable. */ private static <T> T retry(File input, Throwing.Function<File, T> function) { long start = System.currentTimeMillis(); Throwable lastException; do { try { return function.apply(input); } catch (Throwable e) { lastException = e; Errors.suppress().run(() -> Thread.sleep(1)); } } while (System.currentTimeMillis() - start < MS_RETRY); throw Errors.asRuntime(lastException); } }
@Test(expected = AssertionError.class) public void testAssertionPlugin() { DurianPlugins.resetForTesting(); Errors.resetForTesting(); try { // set the Log handler to the be the OnErrorThrowAssertion System.setProperty("durian.plugins.com.diffplug.common.base.Errors.Plugins.Log", "com.diffplug.common.base.Errors$Plugins$OnErrorThrowAssertion"); // send something to the suppress handler, but we should get an AssertionError Errors.log().run(() -> { throw new RuntimeException("Didn't see this coming."); }); } finally { System.clearProperty("durian.plugins.com.diffplug.common.base.Errors.Plugins.Suppress"); DurianPlugins.resetForTesting(); Errors.resetForTesting(); } }
Errors.Rethrowing rethrowing = Errors.createRethrowing(error -> { Errors.Handling handling = Errors.createHandling(error -> { if (error instanceof RuntimeException) { throw (RuntimeException) error;
/** Attempts to run the given runnable. */ public void run(Throwing.Runnable runnable) { wrap(runnable).run(); }
/** Converts this {@code Consumer<Throwable>} to a {@code Consumer<Optional<Throwable>>}. */ public Consumer<Optional<Throwable>> asTerminal() { return errorOpt -> { if (errorOpt.isPresent()) { accept(errorOpt.get()); } }; }
@Test public void testWrapWithDefault() { // function Assert.assertEquals("called", Errors.suppress().wrapWithDefault(input -> "called", "default").apply(null)); Assert.assertEquals("default", Errors.suppress().wrapWithDefault(input -> { throw new IllegalArgumentException(); }, "default").apply(null)); // supplier Assert.assertEquals("called", Errors.suppress().wrapWithDefault(() -> "called", "default").get()); Assert.assertEquals("default", Errors.suppress().wrapWithDefault(() -> { throw new IllegalArgumentException(); }, "default").get()); // predicate Assert.assertEquals(true, Errors.suppress().wrapWithDefault(input -> true, false).test(null)); Assert.assertEquals(false, Errors.suppress().wrapWithDefault(input -> { throw new IllegalArgumentException(); }, false).test(null)); }
/** * Opens a dialog to notify the user of any exceptions. It should be used in cases where * an error is too severe to be silently logged. * <p> * By default, dialog() opens a JOptionPane. To modify this behavior in your application, * call DurianPlugins.set(Errors.Plugins.Dialog.class, error -> openMyDialog(error)); * <p> * For a non-interactive console application, a good implementation of would probably * print the error and call System.exit(). * * @see DurianPlugins * @see Errors.Plugins.OnErrorThrowAssertion */ @SuppressFBWarnings(value = "LI_LAZY_INIT_STATIC", justification = "This race condition is fine, as explained in the comment below.") public static Handling dialog() { if (dialog == null) { // There is an acceptable race condition here. See Errors.log() for details. dialog = createHandling(DurianPlugins.get(Plugins.Dialog.class, Plugins::defaultDialog)); } return dialog; }
/** * Retries an action every ms, for 250ms, until it finally works or fails. * * Makes FS operations more reliable. */ private static <T> T retry(File input, Throwing.Function<File, T> function) { long start = System.currentTimeMillis(); Throwable lastException; do { try { return function.apply(input); } catch (Throwable e) { lastException = e; Errors.suppress().run(() -> Thread.sleep(1)); } } while (System.currentTimeMillis() - start < MS_RETRY); throw Errors.asRuntime(lastException); }
/** Attempts to run the given runnable. */ public void run(Throwing.Runnable runnable) { wrap(runnable).run(); }
/** Converts this {@code Consumer<Throwable>} to a {@code Consumer<Optional<Throwable>>}. */ public Consumer<Optional<Throwable>> asTerminal() { return errorOpt -> { if (errorOpt.isPresent()) { accept(errorOpt.get()); } }; }
/** Calls javaExec() in a way which is friendly with windows classpath limitations. */ public static ExecResult javaExec(Project project, Action<JavaExecSpec> spec) throws IOException { if (OS.getNative().isWindows()) { Box.Nullable<File> classpathJarBox = Box.Nullable.ofNull(); ExecResult execResult = project.javaexec(execSpec -> { // handle the user spec.execute(execSpec); // create a jar which embeds the classpath File classpathJar = toJarWithClasspath(execSpec.getClasspath()); classpathJar.deleteOnExit(); // set the classpath to be just that one jar execSpec.setClasspath(project.files(classpathJar)); // save the jar so it can be deleted later classpathJarBox.set(classpathJar); }); // delete the jar after the task has finished Errors.suppress().run(() -> FileMisc.forceDelete(classpathJarBox.get())); return execResult; } else { return project.javaexec(spec); } }
private static byte[] toByteArray(InputStream in) { ByteArrayOutputStream to = new ByteArrayOutputStream(); byte[] buf = new byte[8192]; try { while (true) { int r = in.read(buf); if (r == -1) { break; } to.write(buf, 0, r); } return to.toByteArray(); } catch (IOException e) { throw Errors.asRuntime(e); } }