import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.net.URI; import java.net.URL; import java.net.URLConnection; import java.util.concurrent.atomic.AtomicBoolean; /** * A example of an asynchronous event handler written from scratch * in Java. The idea here is that we are reading a file. While * the file is being read, we are doing some other work. But we * want to know when the file read completes. Because we are doing * other work, the file reading needs to be in a separate thread. * But want to be able to let the work know that the file is read. * So worker defines a callback function and gives that to the reading * thread. When reading done, reader calls the callback. * @author gtowell * created: Nov 2022 */ public class GTReader { /** * The default callback class. Has one function, onDone * that does nothing. The idea is that anyone who uses * this is going to provide an implementation of onDone */ private class Callback { public void onDone(StringBuffer ssbb) { } } /** * The class that is actually doing the reading. The class * implements runnable, so this is going to do the work in * a separate thread. */ private class AsyncReader implements Runnable { // The URL from which to read private URL uu; // How this thread is going to notify the main thread. private Callback callback; /** * Setup to read file. * @param either a file name or a URL (as a string). The source of the CSV data. * @param callback an instance of the callback class to be used * when read is complete * @throws FileNotFoundException * @throws IOException */ public AsyncReader(String name, Callback callback) throws IOException { this.callback = callback; if (name.contains("://")) { uu = new URL(name); } else { // working with a file name File file = new File(name); URI uri = file.toURI(); uu = uri.toURL(); } } /* * Actually do the file reading. * Note that this does not return anything ... it cant since it * is not started directly but through the Java Thread class. * This indirection beans that the only way this function has to * tell the outside world of it s success is through the callback * */ public void run() { // Since the callback expects to get nack information in a // StringBuffer, create and fill one here StringBuffer sb = new StringBuffer(); BufferedReader buffRead; try { URLConnection huc = uu.openConnection(); huc.setConnectTimeout(30 * 1000); buffRead = new BufferedReader(new InputStreamReader(huc.getInputStream())); while (true) { String temp = buffRead.readLine(); if (temp == null) break; else sb.append(temp); } System.out.println("Read Complete " + Thread.currentThread().getName()); callback.onDone(sb); } catch (IOException ioe) { System.err.println("Reading problem" + ioe); callback.onDone(null); } } } /** * Example of the use of the class * @param args -- ignored */ public static void main(String[] args) { GTReader csvReader = new GTReader(); csvReader.doo(); } // AtomicXXX is a thread-safe version of XXX; in this case boolean. public AtomicBoolean continuu = new AtomicBoolean(true); public void doo() { Callback cb = new Callback() { // Create an instance of Callback with a version of the onDone // method that is specific to this situation! public void onDone(StringBuffer ssbb) { continuu.set(false); if (ssbb!=null) System.out.println(ssbb.length() + " " + Thread.currentThread().getName()); else System.out.println("Read error"); } }; try { int cc = 0; AsyncReader ard = new AsyncReader("https://cs.brynmawr.edu/cs151/Data/HW1/us.csv", cb); new Thread(ard).start(); // start reading while (continuu.get()) { // keep going until read complete try { System.out.println(cc++ + " " + Thread.currentThread().getName()); Thread.sleep(100); // simulate work by sleeping } catch (Exception ee) { } } } catch (IOException ioe) { System.err.println("Ending. Cannot read. " + ioe.toString()); return; } } }