/** * 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); } }
/** An instance of {@code TreeDef.Parented} for {@link Path}. */ public static TreeDef.Parented<Path> forPath(Consumer<Throwable> errorPolicy) { Errors.Handling errors = Errors.createHandling(errorPolicy); return TreeDef.Parented.of( path -> errors.<List<Path>> getWithDefault(() -> { if (Files.isDirectory(path)) { return Files.list(path).collect(Collectors.toList()); } else { return Collections.emptyList(); } }, Collections.emptyList()), path -> errors.<Path> getWithDefault(() -> { return path.getParent(); }, null)); } }
private String toStringSafely(CharSequence csq) { String asString = csq.toString(); if (asString.length() == csq.length()) { return asString; } else { // It's pretty easy to implement CharSequence.toString() incorrectly // http://stackoverflow.com/a/15870428/1153071 // but for String, we know we won't have them, thus the fast-path above Errors.log().accept(new IllegalArgumentException(csq.getClass() + " did not implement toString() correctly.")); char[] chars = new char[csq.length()]; for (int i = 0; i < chars.length; ++i) { chars[i] = csq.charAt(i); } return new String(chars); } }
Food handled = handling.getWithDefault(() -> cook("spaghetti"), CEREAL); // gotta have that default
Errors.log().accept(new IllegalArgumentException("Couldn't find a good monospaced font.")); bestSystemMonospaceFontData = display.getSystemFont().getFontData()[0]; return bestSystemMonospaceFontData;
@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(); } }
/** 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); } }
/** * 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(); } }
@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)); }
@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 }
private String toStringSafely(CharSequence csq) { String asString = csq.toString(); if (asString.length() == csq.length()) { return asString; } else { // It's pretty easy to implement CharSequence.toString() incorrectly // http://stackoverflow.com/a/15870428/1153071 // but for String, we know we won't have them, thus the fast-path above Errors.log().accept(new IllegalArgumentException(csq.getClass() + " did not implement toString() correctly.")); char[] chars = new char[csq.length()]; for (int i = 0; i < chars.length; ++i) { chars[i] = csq.charAt(i); } return new String(chars); } }
private String toStringSafely(CharSequence csq) { String asString = csq.toString(); if (asString.length() == csq.length()) { return asString; } else { // It's pretty easy to implement CharSequence.toString() incorrectly // http://stackoverflow.com/a/15870428/1153071 // but for String, we know we won't have them, thus the fast-path above Errors.log().accept(new IllegalArgumentException(csq.getClass() + " did not implement toString() correctly.")); char[] chars = new char[csq.length()]; for (int i = 0; i < chars.length; ++i) { chars[i] = csq.charAt(i); } return new String(chars); } }
/** An instance of {@code TreeDef.Parented} for {@link Path}. */ public static TreeDef.Parented<Path> forPath(Consumer<Throwable> errorPolicy) { Errors.Handling errors = Errors.createHandling(errorPolicy); return TreeDef.Parented.of( path -> errors.<List<Path>> getWithDefault(() -> { if (Files.isDirectory(path)) { return Files.list(path).collect(Collectors.toList()); } else { return Collections.emptyList(); } }, Collections.emptyList()), path -> errors.<Path> getWithDefault(() -> { return path.getParent(); }, null)); } }
/** An instance of {@code TreeDef.Parented} for {@link Path}. */ public static TreeDef.Parented<Path> forPath(Consumer<Throwable> errorPolicy) { Errors.Handling errors = Errors.createHandling(errorPolicy); return TreeDef.Parented.of( path -> errors.<List<Path>> getWithDefault(() -> { if (Files.isDirectory(path)) { return Files.list(path).collect(Collectors.toList()); } else { return Collections.emptyList(); } }, Collections.emptyList()), path -> errors.<Path> getWithDefault(() -> { return path.getParent(); }, null)); } }
/** An instance of {@code TreeDef.Parented} for {@link File}. */ public static TreeDef.Parented<File> forFile(Consumer<Throwable> errorPolicy) { Errors.Handling errors = Errors.createHandling(errorPolicy); return TreeDef.Parented.of( file -> errors.<List<File>> getWithDefault(() -> { if (file.isDirectory()) { return Arrays.asList(file.listFiles()); } else { return Collections.emptyList(); } }, Collections.emptyList()), file -> errors.<File> getWithDefault(() -> { return file.getParentFile(); }, null)); }
@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); }); }
/** An instance of {@code TreeDef.Parented} for {@link File}. */ public static TreeDef.Parented<File> forFile(Consumer<Throwable> errorPolicy) { Errors.Handling errors = Errors.createHandling(errorPolicy); return TreeDef.Parented.of( file -> errors.<List<File>> getWithDefault(() -> { if (file.isDirectory()) { return Arrays.asList(file.listFiles()); } else { return Collections.emptyList(); } }, Collections.emptyList()), file -> errors.<File> getWithDefault(() -> { return file.getParentFile(); }, null)); }
private void deleteStragglers(File root, Set<File> toKeep, String... dirs) { for (String dir : dirs) { File dirRoot = new File(root, dir); if (dirRoot.exists()) { for (File file : FileMisc.list(dirRoot)) { if (!toKeep.contains(file)) { Errors.log().run(() -> { FileMisc.forceDelete(file); }); } } } } }