Commit 6dbfc1f8 authored by Lubomir Bulej's avatar Lubomir Bulej

Strings: string related utility methods moved from the Files class here.

Files: removed, as it actually only served for string utilities.
Duration: added await() and awaitUninterruptibly() methods.
Duration: softSleep() -> sleepUninterruptibly() for consistency.
Duration: timedWait(), timedJoin() -> wait(), join() since the time is implicit.
Duration: moved to the ch.usi.dag.util package in src-util.
Job: remove duplicated __streamToString() and use Strings.drainStream() instead.
Job: use the generic awaitUninterruptibly() in waitFor().
src-test: reflect the above method and class movement.
parent 73821486
......@@ -8,6 +8,7 @@ import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import ch.usi.dag.util.Duration;
import ch.usi.dag.util.Lists;
......@@ -111,7 +112,7 @@ public class ClientServerEvaluationRunner extends Runner {
__server = __startServer (testInstrJar);
__shadow = __startShadow (testInstrJar);
_INIT_TIME_LIMIT_.softSleep ();
_INIT_TIME_LIMIT_.sleepUninterruptibly ();
if (! __server.isRunning ()) {
throw new IOException ("server failed: "+ __server.getError ());
......
......@@ -8,6 +8,7 @@ import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import ch.usi.dag.util.Duration;
import ch.usi.dag.util.Lists;
public class ClientServerRunner extends Runner {
......@@ -42,7 +43,7 @@ public class ClientServerRunner extends Runner {
) throws IOException {
__server = __startServer (testInstrJar);
_INIT_TIME_LIMIT_.softSleep ();
_INIT_TIME_LIMIT_.sleepUninterruptibly ();
if (! __server.isRunning ()) {
throw new IOException ("server failed: "+ __server.getError ());
......
package ch.usi.dag.disl.test.utils;
import java.util.concurrent.TimeUnit;
/**
*
* @author Lubomir Bulej
*/
public final class Duration {
private final long __amount;
private final TimeUnit __unit;
//
private Duration (final long amount, final TimeUnit unit) {
__amount = amount;
__unit = unit;
}
//
/**
* Converts this {@link Duration} to given time units.
*
* @param unit
* the time unit to convert this {@link Duration} to.
* @return the amount of the given time units representing this
* {@link Duration}.
*/
public long to (final TimeUnit unit) {
return unit.convert (__amount, __unit);
}
//
/**
* @see TimeUnit#timedWait(Object, long)
*/
public void timedWait (final Object object) throws InterruptedException {
__unit.timedWait (object, __amount);
}
/**
* @see TimeUnit#timedJoin(Thread, long)
*/
public void timedJoin (final Thread thread) throws InterruptedException {
__unit.timedJoin (thread, __amount);
}
//
/**
* @see TimeUnit#sleep(long)
*/
public void sleep () throws InterruptedException {
__unit.sleep (__amount);
}
public void softSleep () {
try {
__unit.sleep (__amount);
} catch (final InterruptedException ie) {
// return if interrupted, keeping the interrupted() status
}
}
//
/**
* Creates a {@link Duration} instance representing a given amount of given
* time units.
*
* @param amount
* the amount of time units
* @param unit
* the time unit representing the granularity of the duration
* @return
*/
public static Duration of (final long amount, final TimeUnit unit) {
return new Duration (amount, unit);
}
}
package ch.usi.dag.disl.test.utils;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import ch.usi.dag.util.Duration;
import ch.usi.dag.util.Strings;
import ch.usi.dag.util.function.Predicate;
/**
* Wraps the {@link Process} class provided by Java and adds some more features.
......@@ -46,8 +46,9 @@ final class Job {
private String __error;
/**
* Waits for {@link #__process} to finish, sets {@link #__isRunning} to
* {@code false}, and notifies all objects waiting on {@link #__isRunning}.
* Waits for {@link #__process} to finish. What that happens, sets
* {@link #__isRunning} to {@code false}, and notifies all objects waiting
* on {@link #__isRunning}.
*/
private Thread __waiter;
......@@ -86,6 +87,8 @@ final class Job {
// tricky and hangs the reader. It's better to redirect stdout
// and stderr to files in the forked process.
//
// TODO LB: What exactly does that mean?
//
if (__CLOSE_STREAMS__) {
__process.getInputStream ().close ();
__process.getOutputStream ().close ();
......@@ -194,7 +197,7 @@ final class Job {
//
if (__output == null) {
__output = __streamToString (__process.getInputStream ());
__output = Strings.drainStream (__process.getInputStream ());
}
return __output;
......@@ -224,34 +227,13 @@ final class Job {
//
if (__error == null) {
__error = __streamToString (__process.getErrorStream ());
__error = Strings.drainStream (__process.getErrorStream ());
}
return __error;
}
private static String __streamToString (
final InputStream input
) throws IOException {
final ByteArrayOutputStream output = new ByteArrayOutputStream ();
final int bufferSize = 4096;
final byte [] buffer = new byte [bufferSize];
READ_LOOP: while (true) {
final int bytesRead = input.read (buffer, 0, bufferSize);
if (bytesRead < 1) {
break READ_LOOP;
}
output.write (buffer, 0, bytesRead);
}
return output.toString ();
}
/**
* Waits for a job to finish, for at most the specified time. Returns
* {@code true} if a job is finished or has finished within the given time
......@@ -261,29 +243,23 @@ final class Job {
* the time limit duration
* @param unit
* the unit of the time limit duration
* @return {@code true} if a job is finished, {@code false} otherwise.
* @return {@code true} if a job has finished, {@code false} otherwise.
*/
public boolean waitFor (final Duration duration) {
__ensureJobStarted ();
//
synchronized (__isRunning) {
long remainingNanos = duration.to (NANOSECONDS);
while (__isRunning.get () && remainingNanos > 0) {
try {
final long startTime = System.nanoTime ();
NANOSECONDS.timedWait (__isRunning, remainingNanos);
remainingNanos -= System.nanoTime () - startTime;
return duration.awaitUninterruptibly (__isRunning, __BOOLEAN_IS_FALSE__);
}
} catch (final InterruptedException ie) {
// just return the current state if interrupted
}
private static Predicate <AtomicBoolean> __BOOLEAN_IS_FALSE__ =
new Predicate <AtomicBoolean> () {
@Override
public boolean test (final AtomicBoolean value) {
return !value.get ();
}
return !__isRunning.get ();
}
}
};
/**
......
......@@ -9,7 +9,7 @@ import java.util.List;
import ch.usi.dag.dislreserver.DiSLREServer;
import ch.usi.dag.dislserver.DiSLServer;
import ch.usi.dag.util.Files;
import ch.usi.dag.util.Duration;
import ch.usi.dag.util.Strings;
public abstract class Runner {
......@@ -93,7 +93,7 @@ public abstract class Runner {
//
protected String _loadResource (final String name) throws IOException {
return Files.loadStringResource (__testClass, name);
return Strings.loadFromResource (__testClass, name);
}
protected void _destroyIfRunningAndDumpOutputs (
......@@ -103,13 +103,13 @@ public abstract class Runner {
job.destroy ();
}
Files.storeString (
Strings.storeToFile (
String.format ("tmp.%s.%s.out.txt", __testName, prefix),
job.getOutput ()
);
Files.storeString (
Strings.storeToFile (
String.format ("tmp.%s.%s.err.txt", __testName, prefix),
job.getError ()
);
......
package ch.usi.dag.util;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import java.util.concurrent.TimeUnit;
import ch.usi.dag.util.function.Predicate;
/**
*
* @author Lubomir Bulej
*/
public final class Duration {
private final long __amount;
private final TimeUnit __unit;
//
private Duration (final long amount, final TimeUnit unit) {
__amount = amount;
__unit = unit;
}
//
/**
* Converts this {@link Duration} to the specified time unit.
*
* @param unit
* the time unit to convert this {@link Duration} to.
* @return the amount of the given time units representing this
* {@link Duration}.
*/
public long to (final TimeUnit unit) {
return unit.convert (__amount, __unit);
}
//
/**
* @see TimeUnit#timedWait(Object, long)
*/
public void wait (final Object object) throws InterruptedException {
__unit.timedWait (object, __amount);
}
/**
* @see TimeUnit#timedJoin(Thread, long)
*/
public void join (final Thread thread) throws InterruptedException {
__unit.timedJoin (thread, __amount);
}
//
/**
* @see TimeUnit#sleep(long)
*/
public void sleep () throws InterruptedException {
__unit.sleep (__amount);
}
/**
* Delays the execution of the current thread for this {@link Duration}. If
* interrupted during the sleep, this method resumes the sleep and does not
* return until this {@link Duration} has elapsed. The current thread's
* {@code interrupted} status is not cleared.
*/
public void sleepUninterruptibly () {
long remainingNanos = this.to (NANOSECONDS);
final long timeoutEndNanos = System.nanoTime () + remainingNanos;
while (remainingNanos > 0) {
try {
NANOSECONDS.sleep (remainingNanos);
remainingNanos = timeoutEndNanos - System.nanoTime ();
} catch (final InterruptedException ie) {
// keep sleeping until this duration elapsed
}
}
}
/**
* Suspends the execution of the current thread until either a condition
* becomes true or a timeout expires. The condition is a {@link Predicate}
* to which the target object is passed as an argument. The condition is
* evaluated before waiting on the object and after every wakeup, with the
* target's lock held. The timeout is determined by this {@link Duration}.
*
* @param target
* the object to synchronize on, passed to the {@code condition}
* {@link Predicate}.
* @param condition
* the {@link Predicate} used to evaluate the condition.
* @return the value of the condition at exit, i.e., {@code true} if the
* condition has been met, or {@code false} if the timeout expired.
* @throws InterruptedException
* if a thread has been interrupted while waiting.
*/
public <E> boolean await (
final E target, final Predicate <E> condition
) throws InterruptedException {
synchronized (target) {
if (! __await (target, condition, true)) {
throw new InterruptedException ();
}
return condition.test (target);
}
}
/**
* Suspends the execution of the current thread until either a condition
* becomes true or a timeout expires. The condition is a {@link Predicate}
* to which the target object is passed as an argument. The condition is
* evaluated before waiting on the object and after every wakeup, with the
* target's lock held. The timeout is determined by this {@link Duration}.
* <p>
* In contrast to the {@link #await(Object, Predicate) await} method, if a
* thread is interrupted while waiting on the target object, this method
* keeps waiting until the timeout fully expires.
*
* @param target
* the object to synchronize on, passed to the {@code condition}
* {@link Predicate}.
* @param condition
* the {@link Predicate} used to evaluate the condition.
* @return the value of the condition at exit, i.e., {@code true} if the
* condition has been met, or {@code false} if the timeout expired.
*/
public <E> boolean awaitUninterruptibly (
final E object, final Predicate <E> condition
) {
synchronized (object) {
__await (object, condition, false);
return condition.test (object);
}
}
private <E> boolean __await (
final E object, final Predicate <E> condition,
final boolean isInterruptible
) {
long remainingNanos = this.to (NANOSECONDS);
final long timeoutEndNanos = System.nanoTime () + remainingNanos;
while (!condition.test (object) && remainingNanos > 0) {
try {
NANOSECONDS.timedWait (object, remainingNanos);
remainingNanos = timeoutEndNanos - System.nanoTime ();
} catch (final InterruptedException ie) {
if (isInterruptible) {
return false;
}
}
}
return true;
}
//
/**
* Creates a {@link Duration} instance representing a given amount of given
* time units.
*
* @param amount
* the amount of time units, must be positive
* @param unit
* the time unit representing the granularity of the duration
* @return
*/
public static Duration of (final long amount, final TimeUnit unit) {
if (amount < 0) {
throw new IllegalArgumentException ("amount must be non-negative");
}
return new Duration (amount, unit);
}
}
package ch.usi.dag.util;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
/**
* Utility class providing miscellaneous file- and path-related operations.
*
* @author Lubomir Bulej
*/
public final class Files {
private Files () {
// pure static class - not to be instantiated
}
//
/**
* Stores a string into a file.
*
* @param fileName
* the name of the file to write.
* @param content
* the string to write.
* @throws FileNotFoundException
* if the given file could not be created
* @throws IOException
* if an error occurs while writing to the file
*/
public static void storeString (
final String fileName, final String content
) throws FileNotFoundException {
__storeStringAndClose (new PrintWriter (fileName), content);
}
/**
* Stores a string into a file.
*
* @param fileName
* the name of the file to write.
* @param content
* the string to write.
* @throws FileNotFoundException
* if the given file could not be created
* @throws IOException
* if an error occurs while writing to the file
*/
public static void storeString (
final File fileName, final String content
) throws FileNotFoundException {
__storeStringAndClose (new PrintWriter (fileName), content);
}
private static void __storeStringAndClose (
final PrintWriter output, final String content
) {
try {
output.print (content);
} finally {
output.close ();
}
}
//
/**
* Loads an entire file into a string. This method should be only used for
* reasonable sized text files.
*
* @param fileName
* the name of the file to read
* @return a {@link String} with the contents of the given file
* @throws FileNotFoundException
* if the given file could not be found
* @throws IOException
* if the file could not be read
*/
public static String loadString (final String fileName) throws IOException {
return __drainToStringAndClose (new FileInputStream (fileName));
}
/**
* Loads an entire file into a string. This method should be only used for
* reasonably sized text files.
*
* @param fileName
* the name of the file to read
* @return a {@link String} with the contents of the given file
* @throws FileNotFoundException
* if the given file could not be found
* @throws IOException
* if the file could not be read
*/
public static String loadString (final File fileName) throws IOException {
return __drainToStringAndClose (new FileInputStream (fileName));
}
/**
* Loads an entire resource associated with a given class into a string.
* This method should be only used for reasonably sized text resources.
*
* @param refClass
* the reference class to use when looking for an associated resource
* @param name
* the name of the resource to read
* @return a {@link String} with the contents of the given resource, or
* {@code null} if the resource could not be found
* @throws FileNotFoundException
* if the given resource could not be found
* @throws IOException
* if the resource could not be read
*/
public static String loadStringResource (
final Class <?> refClass, final String name
) throws IOException {
final InputStream input = refClass.getResourceAsStream (name);
if (input != null) {
return __drainToStringAndClose (input);
} else {
throw new FileNotFoundException ("no such resource: "+ name);
}
}
private static String __drainToStringAndClose (
final InputStream input
) throws IOException {
try {
return __drainToString (input);
} finally {
input.close ();
}
}
private static String __drainToString (
final InputStream input
) throws IOException {
final ByteArrayOutputStream output = new ByteArrayOutputStream ();
final int bufferSize = 4096;
final byte [] buffer = new byte [bufferSize];
READ_LOOP: while (true) {
final int bytesRead = input.read (buffer, 0, bufferSize);
if (bytesRead < 1) {
break READ_LOOP;
}
output.write (buffer, 0, bytesRead);
}
return output.toString ();
}
}
package ch.usi.dag.util;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
/**
* Utility class providing miscellaneous string operations.
*
......@@ -109,4 +117,152 @@ public final class Strings {
return builder.toString ();
}
//
public static String drainStream (
final InputStream input
) throws IOException {
final ByteArrayOutputStream output = new ByteArrayOutputStream ();
final int bufferSize = 4096;
final byte [] buffer = new byte [bufferSize];
READ_LOOP: while (true) {
final int bytesRead = input.read (buffer, 0, bufferSize);
if (bytesRead < 1) {
break READ_LOOP;
}
output.write (buffer, 0, bytesRead);
}
return output.toString ();
}
//
/**
* Loads an entire file into a string. This method should be only used for
* reasonable sized text files.
*
* @param fileName
* the name of the file to read
* @return a {@link String} with the contents of the given file
* @throws FileNotFoundException
* if the given file could not be found
* @throws IOException
* if the file could not be read
*/
public static String loadFromFile (final String fileName) throws IOException {
return __drainStreamAndClose (new FileInputStream (fileName));
}
/**
* Loads an entire file into a string. This method should be only used for
* reasonably sized text files.
*
* @param fileName
* the name of the file to read
* @return a {@link String} with the contents of the given file
* @throws FileNotFoundException
* if the given file could not be found
* @throws IOException
* if the file could not be read
*/
public static String loadFromFile (final File fileName) throws IOException {
return __drainStreamAndClose (new FileInputStream (fileName));
}
/**
* Loads an entire resource associated with a given class into a string.
* This method should be only used for reasonably sized text resources.
*
* @param refClass
* the reference class to use when looking for an associated resource
* @param name
* the name of the resource to read
* @return a {@link String} with the contents of the given resource, or
* {@code null} if the resource could not be found
* @throws FileNotFoundException
* if the given resource could not be found
* @throws IOException
* if the resource could not be read
*/
public static String loadFromResource (
final Class <?> refClass, final String name
) throws IOException {
final InputStream input = refClass.getResourceAsStream (name);
if (input != null) {
return __drainStreamAndClose (input);
} else {
throw new FileNotFoundException ("no such resource: "+ name);
}