diff --git a/hadoop-ozone/tools/pom.xml b/hadoop-ozone/tools/pom.xml index ac819b50109..2d273d1349a 100644 --- a/hadoop-ozone/tools/pom.xml +++ b/hadoop-ozone/tools/pom.xml @@ -77,6 +77,12 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd"> test test-jar + + org.mockito + mockito-core + 2.15.0 + test + diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/freon/ProgressBar.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/freon/ProgressBar.java new file mode 100644 index 00000000000..a8d7e73c73a --- /dev/null +++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/freon/ProgressBar.java @@ -0,0 +1,210 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.hadoop.ozone.freon; + +import java.io.PrintStream; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * Run an arbitrary code and print progress on the provided stream. The + * progressbar stops when: - the provided currentvalue is less the the maxvalue + * - exception thrown + */ +public class ProgressBar { + + private static final long REFRESH_INTERVAL = 1000L; + + private PrintStream stream; + private AtomicLong currentValue; + private long maxValue; + private Thread progressBar; + private volatile boolean exception = false; + private long startTime; + + /** + * @param stream Used to display the progress + * @param maxValue Maximum value of the progress + */ + ProgressBar(PrintStream stream, long maxValue) { + this.stream = stream; + this.maxValue = maxValue; + this.currentValue = new AtomicLong(0); + this.progressBar = new Thread(new ProgressBarThread()); + } + + /** + * Start a task with a progessbar without any in/out parameters Runnable used + * just a task wrapper. + * + * @param task Runnable + */ + public void start(Runnable task) { + + startTime = System.nanoTime(); + + try { + + progressBar.start(); + task.run(); + + } catch (Exception e) { + exception = true; + } finally { + + try { + progressBar.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + /** + * Start a task with only out parameters. + * + * @param task Supplier that represents the task + * @param Generic return type + * @return Whatever the supllier produces + */ + public T start(Supplier task) { + + startTime = System.nanoTime(); + T result = null; + + try { + + progressBar.start(); + result = task.get(); + + } catch (Exception e) { + exception = true; + } finally { + + try { + progressBar.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + return result; + } + } + + /** + * Start a task with in/out parameters. + * + * @param input Input of the function + * @param task A Function that does the task + * @param type of the input + * @param return type + * @return Whatever the Function returns + */ + public R start(T input, Function task) { + + startTime = System.nanoTime(); + R result = null; + + try { + + progressBar.start(); + result = task.apply(input); + + } catch (Exception e) { + exception = true; + throw e; + } finally { + + try { + progressBar.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + return result; + } + } + + /** + * Increment the progress with one step. + */ + public void incrementProgress() { + currentValue.incrementAndGet(); + } + + private class ProgressBarThread implements Runnable { + + @Override + public void run() { + try { + + stream.println(); + long value; + + while ((value = currentValue.get()) < maxValue) { + print(value); + + if (exception) { + break; + } + Thread.sleep(REFRESH_INTERVAL); + } + + if (exception) { + stream.println(); + stream.println("Incomplete termination, " + "check log for " + + "exception."); + } else { + print(maxValue); + } + stream.println(); + } catch (InterruptedException e) { + stream.println(e); + } + } + + /** + * Given current value prints the progress bar. + * + * @param value current progress position + */ + private void print(long value) { + stream.print('\r'); + double percent = 100.0 * value / maxValue; + StringBuilder sb = new StringBuilder(); + sb.append(" " + String.format("%.2f", percent) + "% |"); + + for (int i = 0; i <= percent; i++) { + sb.append('█'); + } + for (int j = 0; j < 100 - percent; j++) { + sb.append(' '); + } + sb.append("| "); + sb.append(value + "/" + maxValue); + long timeInSec = TimeUnit.SECONDS.convert( + System.nanoTime() - startTime, TimeUnit.NANOSECONDS); + String timeToPrint = String.format("%d:%02d:%02d", timeInSec / 3600, + (timeInSec % 3600) / 60, timeInSec % 60); + sb.append(" Time: " + timeToPrint); + stream.print(sb.toString()); + } + } + +} diff --git a/hadoop-ozone/tools/src/test/java/org/apache/hadoop/ozone/freon/TestProgressBar.java b/hadoop-ozone/tools/src/test/java/org/apache/hadoop/ozone/freon/TestProgressBar.java new file mode 100644 index 00000000000..cea7be8183d --- /dev/null +++ b/hadoop-ozone/tools/src/test/java/org/apache/hadoop/ozone/freon/TestProgressBar.java @@ -0,0 +1,112 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.hadoop.ozone.freon; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import java.io.PrintStream; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.IntStream; + +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +/** + * Tests for the Progressbar class for Freon. + */ +public class TestProgressBar { + + private PrintStream stream; + + @Before + public void setupMock() { + stream = mock(PrintStream.class); + } + + @Test + public void testWithRunnable() { + + int maxValue = 10; + + ProgressBar progressbar = new ProgressBar(stream, maxValue); + + Runnable task = () -> { + IntStream.range(0, maxValue).forEach( + counter -> { + progressbar.incrementProgress(); + } + ); + }; + + progressbar.start(task); + + verify(stream, atLeastOnce()).print(anyChar()); + verify(stream, atLeastOnce()).print(anyString()); + } + + @Test + public void testWithSupplier() { + + int maxValue = 10; + + ProgressBar progressbar = new ProgressBar(stream, maxValue); + + Supplier tasks = () -> { + IntStream.range(0, maxValue).forEach( + counter -> { + progressbar.incrementProgress(); + } + ); + + return 1L; //return the result of the dummy task + }; + + progressbar.start(tasks); + + verify(stream, atLeastOnce()).print(anyChar()); + verify(stream, atLeastOnce()).print(anyString()); + } + + @Test + public void testWithFunction() { + + int maxValue = 10; + Long result; + + ProgressBar progressbar = new ProgressBar(stream, maxValue); + + Function task = (Long l) -> { + IntStream.range(0, maxValue).forEach( + counter -> { + progressbar.incrementProgress(); + } + ); + + return "dummy result"; //return the result of the dummy task + }; + + progressbar.start(1L, task); + + verify(stream, atLeastOnce()).print(anyChar()); + verify(stream, atLeastOnce()).print(anyString()); + } + +}