Skip to content

Latest commit

 

History

History
executable file
·
118 lines (75 loc) · 9.85 KB

b33f.md

File metadata and controls

executable file
·
118 lines (75 loc) · 9.85 KB

Back to questions

b33f: Logging using a functional interface

This question will give you practice with quite a range of Java features! You are going to write two functional interfaces: a logger interface, with a method for logging a message at a given severity level, and a string inspector interface, with a method that takes a string and optionally returns a (severity level, message) pair related to the string's content.

You are also going to implement a couple of loggers and string inspectors, using direct implementations and lambdas. The problem will illustrate how we can use interfaces to provide abstract notions of logging and string inspection, and implement an algorithm that can inspect all lines of a file and log the results, and that we can instantiate this algorithm with various different notions of string inspection and various different methods for logging.

  1. Immutable pair. [Note: if you tried question 888a you may copy in the ImmutablePair class implemented as part of that question.] Write a generic class, ImmutablePair, with two generic parameters, S and T say, that allows the construction of an (S, T) pair, and retrieval of its first and second components. Override toString() so that a pair is represented as a string of the form (string representation of first element, string representation of second element).

  2. Log level enum. Write an enumeration class, LogLevel, specifying the following severity levels, in increasing order of severity:

    • VERBOSE
    • INFO
    • WARNING
    • ERROR
    • FATAL
  3. Logger functional interface. Write a functional interface, Logger, specifying one method: void log(LogLevel logLevel, String message); Use an annotation to indicate that this is a functional interface. Check that, with your annotation, you get a compiler error if you try to add another method to the interface.

  4. String inspector functional interface. Write a functional interface, StringInspector, specifying a single method, inspect, that takes a string and returns, optionally, an ImmutablePair comprised of a LogLevel and a string.

    The idea is that this method will inspect the input string, returning nothing (i.e. an empty optional) if the string is not noteworthy, and a severity level and message if the string is noteworthy. It is up to implementations of this interface to decide what "noteworthy" means in practice.

  5. File inspector class. Write a class, FileInspector, with a single field of type Logger, a constructor that populates this field with a given Logger reference, and a single method, inspectFile, that takes a file name (string) and a StringInspector.

    The method should open the file with the given name, and process it line by line. It should invoke the string inspector for each line, and if the string inspector reports that the line is noteworthy, it should invoke the logger with the severity level and message reported by the string inspector.

    You can declare the inspectFile method should be declared as throwing IOException, to avoid having to deal with exceptions, but feel free to experiment with using a try...catch block, e.g. to ignore exceptions, or log a fatal error if an exception occurs.

  6. File logger implementation. Write a class, FileLogger, that implements the Logger interface. This class should have a BufferedWriter field, and should be constructed with a file name: the file name should be used to create a buffered reader from a file reader for the given file, throwing an IOException when something goes wrong.

    The log method should do nothing if the given severity level is INFO or VERBOSE. Otherwise it should write a line to the buffered writer of the form "SEVERITY_LEVEL: message", where SEVERITY_LEVEL is the string representation of the given severity level and message is the message string.

    If an IOException occurs during the execution of log, it should be ignored.

    The class should also have a close method that closes the buffered writer, throwing an IOException if something goes wrong.

  7. Known words string inspector implementation. Write a class, KnownWordsStringInspector, that implements the StringInspector interface. As fields, this class should have a set of error words and a set of verbose words, each of which should be a set of strings, and the class should be constructed with a reference with which to populate each of these fields.

    The inspect method should split the input string using " ", turning it into an array of strings, and iterate over these strings. Each string that occurs in the set of error words should be added to a list of observed error words. Each string that occurs in the set of verbose words should be added to a list of observed verbose words. If no error or verbose words are observed, the method should return an empty optional. Otherwise, a (severity level, message) pair should be returned (packaged in an optional). The severity level should be ERROR if at least one error word was observed, and VERBOSE otherwise. The message should indicate which error words and which verbose words were observed.

  8. Writing a demo, using these classes and using some lambdas. Write a class, Demo, with a main method.

    In the main method, check that exactly three arguments are supplied; these should correspond to an input file and two output files.

    Declare a local variable, stderrLogger, of type Logger, that implements the functional interface Logger via a lambda:

    final Logger stderrLogger = (logLevel, message) -> { ... };
    

    The body of this lambda should write message to standard error, prefixed by ***IMPORTANT***\n if the severity level is WARNING or higher, and prefixed by *NOTE*\n otherwise.

    Similarly, declare a local variable, lineTooLongInspector, of type StringInspector, that implements the functional interface StringInspector via a lambda. The lambda should return an optional containing a pair of the form (ERROR, "Line too long: line") if the given string is more than 100 characters in length, and should return an empty optional otherwise.

    Create three FileInspector objects:

    - A `FileInspector` that takes `stderrLogger` as its logger
    - Two `FileInspectors` that each take a `FileLogger` as their logger, using `args[1]` and `args[2]` as the output file names
    

    Create a KnownWordsStringInspector with some error and verbose words of your choice.

    Use the following statements to run your file inspectors on the file with name args[0], using your string inspectors:

     System.err.println("Starting inspection 1:");
     inspector1.inspectFile(args[0], lineTooLongInspector);
     System.err.println("\nStarting inspection 2:");
     inspector1.inspectFile(args[0], knownWordsInspector);
     System.err.println("\nStarting inspection 3 (see " + args[1] + " for details).");
     inspector2.inspectFile(args[0], lineTooLongInspector);
     System.err.println("\nStarting inspection 4 (see " + args[2] + " for details).");
     inspector3.inspectFile(args[0], knownWordsInspector);
    

    At the end of main, be sure to close the two FileLogger instances that you created.

  9. Running your program. For my "known words" string inspector I used "goto", "finalize" and "static" as the error words, and "continue" and "break" as the verbose words. I populated "input.txt" with the following:

     the quick brown fox jumped over the lazy dog
     a b c d e f g h i j k l m n o p q r s t u v w x y z now I know my abc next time won't you sing with me?
     Dijkstra said that goto is considered harmful; OS developers disagree
     I want to goto a warmer place and stop switch ing between tasks so often, all this switch ing makes it hard to finalize anything, and can't continue like this without at least a short break
     But for now I will continue to continue until that break comes
    

    I then ran this command to execute the program:

     java tutorialquestions.questionb33f.Demo input.txt output1.txt output2.txt
    

    which gave the following output (the output your program gives may of course be a bit different from this, depending on exactly how you chose to emit strings):

     Starting inspection 1:
     *** IMPORTANT ***
     Line too long: a b c d e f g h i j k l m n o p q r s t u v w x y z now I know my abc next time won't you sing with me?
     *** IMPORTANT ***
     Line too long: I want to goto a warmer place and stop switch ing between tasks so often, all this switch ing makes it hard to finalize anything, and can't continue like this without at least a short break
    
     Starting inspection 2:
     *** IMPORTANT ***
     Observed the following error words: [goto]
    
     *** IMPORTANT ***
     Observed the following error words: [goto, finalize]
     Observed the following words of note: [continue, break]
    
     * NOTE *
     Observed the following words of note: [continue, continue, break]
    
    
     Starting inspection 3 (see output1.txt for details).
    
     Starting inspection 4 (see output2.txt for details).
    

    Contents of "output1.txt":

     ERROR: Line too long: a b c d e f g h i j k l m n o p q r s t u v w x y z now I know my abc next time won't you sing with me?
     ERROR: Line too long: I want to goto a warmer place and stop switch ing between tasks so often, all this switch ing makes it hard to finalize anything, and can't continue like this without at least a short break
    

    Contents of "output2.txt":

     ERROR: Observed the following error words: [goto]
    
     ERROR: Observed the following error words: [goto, finalize]
     Observed the following words of note: [continue, break]