# Socket Chat Program

-   Assignment Due: June 23, 2020 at 11:00am (CDT, UTC-5)
-   Peer Assessment Due: June 23, 2020 at 11:59pm

In this assignment you will modify a simple 2-way network chat program to
prompt the user and get responses in a language other than English, and to
provide rudimentary protection of the messages passing over the network.

You will also perform *whitebox testing* of part of your code. In the
Calculator assignment you developed a set of test cases based on the blackbox
specification. Rarely will such a test suite fully exercise the code. You will
develop a test suite such that each statement in the code under test is
executed at least once.  (Note also that whitebox testing will rarely cover the
blackbox specification. In practice, a combination of blackbox and whitebox
testing is necessary.)

### Objectives

Students will:

-   Demonstrate good software engineering practices
-   Learn two design patterns
    -   Strategy Pattern
    -   Simple Factory Pattern
-   Make use of resource "property" files to support internationalization
-   Develop whitebox test cases to attain statement coverage
-   Practice using test doubles
-   Create JUnit tests for those test cases
-   Practice overriding the default author on a git commit

## Instructions

This assignment is to be completed in assigned pairs; **no collaboration
other than with your assigned partner is permitted**.  One of the purposes of
pair-assignments is to practice teamwork. After completing the assignment you
will need to complete a peer assessment. Your contribution grade will be based
on the peer assessments and on the git history.

*Commit material that you worked on individually under your own name* using the
defaults that you set. *When (and only when) you commit material that was
developed using pair programming, override the default commit author to reflect
both authors* so that we can properly credit both authors for their contribution
grades. When you override the default commit author list both students' names,
and for the email address use a fake email address that is unique to the pair
of students by concatenating your Canvas login IDs (the angle brackets around
the email address are required):
```
git commit --author="Herbie Husker and Lil Red <hhusker20lred19@dev.null>"
```
You can use this same technique for the rare circumstance in which your partner
is briefly unable to commit code themselves:
```
git commit --author="Herbie Husker <herbie@huskers.unl.edu>"
```

## Setup

1.  You and your partner will work on a shared repository, which has been
    prepared for you.

    1.  Navigate to your shared directory
        (<https://git.unl.edu/csce_361/summer2020/09pairNN/>, where *NN* is your
        team number).

    1.  Verify that the repository is private, and that you and your partner
        both have Maintainer access.

1.  Both students should:

    1.  Clone the project: `git clone <URL>` (here the angle brackets should
        not be included).

        -   **Do *NOT* place your socket_chat repository inside your
            csce361-homework repository!**

    1.  Import the project into your IDE. The project is set up as a Maven
        project, so you can follow your IDE's instructions to import a Maven
        project.

## Issue Tracker

We have pre-populated your repository's Issue Tracker with issues for the
various parts of the assignment that need to be completed. **We do *not*
guarantee that the pre-populated issues are complete; this document is the
authoritative source of requirements.**

-   A good way to coordinate who is working on which parts of the code is to
    use the [web interface](https://docs.gitlab.com/ee/user/project/issues/index.html#issue-page)
    to "assign" an issue to a team member.
-   You may [add more issues](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#create-a-new-issue)
    to the Issue Tracker if you wish. This is common when you discover more
    tasks that need to be accomplished or when you want to divide an existing
    issue into finer-grained tasks (the original issue would still exist, but
    the finer-grained issues may make it easier to divide the work).
-   When you have completed a task, [close the corresponding issue](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues).
    You can do this through the issue tracker, or you can do it through a
    commit message.
    -   To close an issue through a commit message, include a keyword such as
        `closes #12` in a commit message. You cannot create a commit message
        only to close an issue; this must be a commit message for adding/
        changing/removing files or for a merge.  See <https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#default-closing-pattern>
        for keywords that will close an issue.

## Assignment

Look over the starter code, view
<!--[this short demonstration](https://use.vg/qB6zFZ),-->
[this short demonstration](TODO) <!-- this one includes relay server -->
and run the program to get a
feel for how the program works.

-   One host, one client, but the same code
-   Normal usage (requires host and client to be on the same network):
    -   To avoid having to do any network discovery, the host displays its IP
        address, which must be shared with whoever is running the client so they
        can type in the IP address
    -   The host sets the port number to be used, and this must be shared with
        whoever is running the client so they can type in the IP address
        -   If the port number were hard-coded then we'd run the risk of that
            port number already being in use
    -   The host sets up a `ServerSocket` to accept the client's connection, and
        the client sets up a `Socket` to connect to the host
-   Remote instruction usage:
    -   We have provided a Relay Server on csce.unl.edu, whose IP address is
        publicly visible, so it doesn't have to be on the same network as the
        chatters
    -   When launching SocketChat, both chatters should select the option to be
        the client
    -   IP address: <!--`-127.93.-91.26`--> `129.93.165.26`, Port: `361NN`,
        where *NN* is your team number
-   The two chatters alternate turns sending Strings to each other over the
    socket
    -   The host (or the first chatter to connect to the Relay Sever) sends the
        first message
-   When there's message from either chatter consisting solely of the keyword
    `EXIT` in all capital letters, the program terminates.

<!--
NOTE: in the demonstration, I used jar files. This was convenient for the
purposes of the demonstration. You do not have to create jar files, but if you
want to, you can use Maven's "package" target. (Separate instructions for this
will be posted on Piazza.)
-->

### Internationalization

For successful software, it's almost certain that there will be people who want
to use your software who don't speak the same language as you. The process of
making your software work with any human language is called
*internationalization*, often abbreviated as *I18N*.

A common technique for I18N is to use a `ResourceBundle` of key-value pairs.
Notice that anyplace that the program outputs something, the argument to
`println()` is `bundle.getString(...)` -- that substitutes the value String
that corresponds to the specified key String. We also use this to compare the
user input to a string.

The key-value pairs are in a properties file in the `resources/` directory.
The files are of the form `basename_XX.properties`, where XX is the 2- or 3-
character language code. Example language codes are `en` for English, `fr` for
French, `de` for German, `zh` for Chinese, and `tlh` for Klingon. If you decide
to do full localization then the suffix includes a language code, a country
code, and possibly a variant code. We'll limit ourselves to just the language
code. Since the basename we're using is "socketchat", the file with the English-
language strings is `socketchat_en.properties`.

-   The repository includes what may appear to be a file simply named
    `socketchat.properties`. **Do not directly edit this file**.

Notice that some of the value strings have numbered arguments such as `{0}` and
`{1}`. When combined with `MessageFormat.format()`, this allows us to create
parameterized strings similar to C's `printf()`.

1.  Create a properties file for another language. If you don't know another
    language, you may use an electronic (or online) translator or bilingual
    dictionary for this assignment.
    -   The language codes can be found in
        [ISO 639-2](https://www.loc.gov/standards/iso639-2/php/code_list.php).
        If both a 2-letter and a 3-letter language code are available for the
        language you choose, you *must* use the 2-letter code.
    -   You *must* use the same keys as the English-language properties file,
        because the program depends on the keys.

1.  Decide on a keyword to indicate that you want to change the language. As
    with the `EXIT` keyword, this keyword will be typed as part of the chat.
    Add this keyword in English to `socket_chat_en.properties` and in the other
    language's properties file, using the key `communicate.keyword.setLocale`.

1.  Un-comment the commented-out code in `Chat.handleKeyword()` and edit it to
    handle the new keyword.
    -   Find out what language the user wants to change to.
    -   At a minimum, you must handle the cases where the user wants to change
        to English and where the user wants to change to the language you chose
        in Step 1.
    -   After you have determined which language the user wants to change to,
        call `Chat.setLocale()`
        -   Use `Locale.ENGLISH` or `Locale.US` if the user wants to change to
            English.
        -   If you earlier chose Chinese, French, German, Italian, Japanese, or
            Korean, you can use `Locale.CHINESE`, `Locale.FRENCH`,
            `Locale.GERMAN`, `Locale.ITALIAN`, `Locale.JAPANESE`, or
            `Locale.KOREAN`.
        -   If you earlier chose a language listed in the first column of the
            table on [this webpage](https://www.oracle.com/technetwork/java/javase/javase7locales-334809.html),
            then you can use the `Locale.Locale(String language)` constructor,
            where `language` is the 2-character language code at the start of
            the Locale ID in the third column of that table. Or use
            `Locale.Locale(String language, String country)` where `country` is
            the 2-character country code at the end of the Locale ID in the
            third column. Or (rarely), `Locale.Locale(String language, String
            country, String variant)`.
        -   Otherwise, create a `Locale` using [`Locale.Builder`](https://docs.oracle.com/javase/8/docs/api/java/util/Locale.Builder.html).
<!--            (Yes, this is using the Builder Pattern.) -->

### Whitebox Testing

This is the specification for `Chat.communicateOneMessage()` (not including
exception handling):
-   The method will return `true` unless otherwise noted below
-   If the message to be processed originates locally then the contents of
    `localInput` shall be enciphered and be placed on `remoteOutput`
-   If the message to be processed originates remotely then the contents of
    `remoteInput` shall be deciphered and be placed on `localOutput`
-   If a remotely-originated message is `null`, then an error message shall be
    placed on `localOutput` indicating that a null message was received, and
    the method will return `false`
-   If the message, regardless of origin, consists solely of a keyword, then
    the message shall be passed to the `handleKeyword()` method for processing;
    the `communicateOneMessage()` method will return the boolean value returned
    by `handleKeyword()`

4.  Write sufficient JUnit tests to attain *statement coverage* of
    `communicateOneMessage()`

    -   You will need to prepare test doubles to be able to run automated tests
        of `communicateOneMessage()`

    -   The exception-handling behavior is not part of the specification, but
        the developers wisely attempted to gracefully handle exceptional
        conditions; you will need to write tests to exercise the catch block
        for a `SocketException` (we have provided a test for the `IOException`
        catch block)

    -   You can confirm that you have statement coverage by generating a
        coverage report
        -   Eclipse: <https://www.eclemma.org/userdoc/importexport.html>
            -   Instructions at the bottom of that page. Export as HTML
        -   IntelliJ IDEA: <https://www.jetbrains.com/help/idea/generating-code-coverage-report.html>

### Strategy Pattern

You will use the *Strategy Pattern* to attach cipher algorithms to the chat
program.[^1]

-   HFDP, [Chapter 1](https://learning.oreilly.com/library/view/head-first-design/0596007124/ch01.html)

[^1]:   For this application, you are going to write cipher algorithms and
        attach them to the chat program using the Strategy Pattern, so that you
        can learn the Strategy Pattern. For a real application, I strongly
        advise against writing your own cipher algorithms. Instead use
        `javax.crypto.Cipher` and, if you're streaming text back and forth as
        in this program, attach the algorithms using the Decorator pattern via
        `javax.crypto.CipherInputStream` and `javax.crypto.CipherOutputStream`.
        (for the Decorator Pattern, see HFDP,
        [Chapter 3](https://learning.oreilly.com/library/view/head-first-design/0596007124/ch03.html))

The original version of this program sends plaintext messages over the network.
The current version passes an outgoing message through `Chat.encipher()` and
incoming messages through `Chat.decipher()`. Right now, all these methods do is
return the original message without enciphering (or deciphering) it.

5.  Create `Cipher.java`, an interface with two methods: `String
    encipher(String plaintext)` whose specification is that it that passes the
    plaintext through a cipher to create ciphertext, and `String
    decipher(String ciphertext)` whose specification is that it passes the
    cipher text through the inverse cipher to produce the original plaintext.

1.  Create `NullCipher.java`, a class that implements the `Cipher` interface.
    `NullCipher.encipher()` and `NullCipher.decipher()` should simply return
    their argument without modification (just like `Chat.encipher()` and
    `Chat.decipher()` do now.)

1.  Create a field in `Chat.java` for the cipher behavior. You might call it
    `Cipher cipherBehavior` or `Cipher cipherStrategy` -- or you might simply
    call it `Cipher cipher`, but by giving it a name with the word *Behavior*
    or *Strategy* in it, you're flagging the existence of the Strategy Pattern
    to anybody who reads your code.
    -   In the `Chat.Chat()` constructor, initialize this field to a
        `NullCipher` object.

1.  Replace the code in `Chat.encipher()` and `Chat.decipher()` with:
    ```
    private String encipher(String plaintext) {
        String ciphertext = cipherBehavior.encipher(plaintext);
        return ciphertext;
    }

    private String decipher (String ciphertext) {
        String plaintext = cipherBehavior.decipher(ciphertext);
        return plaintext;
    }
    ```
    (here I assumed you named the `Cipher` field `cipherBehavior`. If you
    didn't, the substitute the actual name you used.)

At this point, the program should still have the same externally-observable
behavior it had after you finished Step 3. What's different is that,
internally, you've delegated the behavior for `Chat.encipher()` and
`Chat.decipher()` to another object.

This would be a good time to verify that your partial implementation of the
Strategy Pattern hasn't broken anything.

9.  Re-run your test suite to make sure no changes caused your tests to fail.

1.  Implement two classical ciphers as Java classes that implement the `Cipher`
    interface. You can include any other methods you feel are necessary;
    however, only `encipher()` and `decipher()` will be exposed to `Chat.java`.
    -   You may use ciphers you implemented in a previous course (but they must
        be written in Java) or [other
        ciphers](http://practicalcryptography.com/ciphers/classical-era/).
    -   You probably want to have the key(s) be arguments to the constructors,
        but feel free to explore other options.
    -   These must be different ciphers. Implementing the same cipher with
        different keys will be treated as having implemented only one cipher.
        For example, a Caesar cipher with a shift of 5 and a Caesar cipher with
        a shift of 16 are the same cipher with different keys.

1.  Write JUnit tests to verify that your `encipher()` and `decipher()` methods
    function correctly.

1.  You can replace `NullCipher` in the `Chat` constructor with either of the
    other ciphers you wrote, and your program will still work.

1.  If `NullCipher` is no longer the initial cipher then make any changes to
    your tests that are necessary to reflect this new behavior
    -   The option that requires the least effort in the short term would be to
        change the input and/or expected output for some tests to take into
        account the new cipher
    -   A better option would be to make any changes needed to your code to
        allow your tests to set the cipher being used, then set the cipher
        either in your tests or in your `setUp()`, and finally change the input
        and/or expected output for some tests to take into account the assigned
        cipher
        -   While this will require a little more effort in the short term, it
            means you won't have to change your tests again if you (or somebody
            else) later decides to change the initial cipher
    -   Run your test suite to make sure everything passes

What we need now to complete the Strategy Pattern is a way to replace the
cipher algorithm at runtime. Many options are possible; we'll use the...

### Simple Factory Pattern

You will use the *Simple Factory Pattern* to obtain the cipher algorithms

-   HFDP, [Chapter 4](https://learning.oreilly.com/library/view/head-first-design/0596007124/ch04.html)
-   The authors of *Head First Design Patterns* say it isn't a real pattern
    but instead is a commonly-used idiom. This is a distinction without a
    difference: a pattern is a common solution to a common problem, and an
    idiom is a common way to express an idea in code.

The Simple Factory is not nearly as powerful as the Abstract Factory Pattern,
or even the Factory Method Pattern, but I sure seem to use it often. There is
no one-true form of the Simple Factory. Sometimes it takes no arguments and
solely exists to hide which concrete class is being used. Other times, it will
be parameterized and the particular concrete class that is returned depends on
the arguments used. Another option yet is to have a method that sets the
concrete type, and then the `create` method will always return that concrete
type -- but because the `create` method is declared to return the abstraction,
the client code cannot assume a concrete type.

We will use a parameterized factory.

14. Create `CipherFactory.java`.

1.  Write the method
    `public static Cipher createCipher(String name, String[] keys)`. This
    method shall create and return an instance of a `Cipher` implementation
    class that corresponds to `name`. If that cipher only requires one key,
    then this method will use `keys[0]` as that key; if the cipher requires
    multiple keys, then it will use the appropriate number of elements from
    `keys` as the cipher keys. If that cipher requires no keys, then this
    method will ignore `keys`.

1.  Write the method `public static Cipher createCipher()` without any
    arguments. This method shall return a default cipher algorithm. (You decide
    what that default is).

That's it. That's your Simple Factory.

Now it's time to use it.

17. Find line line in the `Chat.Chat()` constructor where you wrote
    `cipherBehavior = new NullCipher()` (or something like that). Replace
    `new NullCipher()` with `CipherFactory.createCipher()`. Now your cipher
    algorithm is whatever the default happens to be.

    -   At this point, nothing in `Chat.java` should depend on any particular
        implementation of the `Cipher` interface.

1.  Decide on a keyword to indicate that you want to change the cipher
    algorithm. As with the `EXIT` keyword, this keyword will be typed as part
    of the chat. Add this keyword in English to `socket_chat_en.properties` and
    in the other language's properties file, using a key of your choosing (but
    make sure it starts with `communicate.keyword.` so that the code to detect
    keywords knows to look for it).

1.  Add code to `Chat.handleKeyword()` to handle the new keyword. This code
    should work with the user to change the cipher algorithm to whichever
    cipher algorithm they want, with the key(s) they want.

    Nothing in your code should directly or indirectly depend on
    implementations of `Cipher`.

    -   There should be no direct dependence: the concrete `Cipher`s should not
        even be mentioned anywhere in Chat.java.
    -   There should be no indirect dependence: you should not have conditional
        statements to determine what arguments to pass to the `CipherFactory`.
    -   You can check whether you attained this by answering the following
        question: If you write another cipher, will you have to add or modify
        any code in `Chat.java`? Obviously you will have to add code to
        `CipherFactory.java`. If you have to add or modify `Chat.java` then
        your code still depends on knowledge of `Cipher` implementations.

1.  Re-run your test suite to make sure no changes caused your tests to fail.

You can now chat away without worrying about a "l337 h4x0r" being snoopy. (If
you're worried about someone with NSA-level snooping capabilities, don't use a
"classical era" cipher! They're relatively easy to break in general, and the
keywords would help someone trying to break it do so by giving known words to
look for. The "EXIT" keyword especially so, since it occurs at the end of every
conversation.)

You might wonder why we used a Simple Factory to create Cipher objects instead
of just putting the same logic in a method in the `Chat` class. There are three
reasons. One is that there might be other projects that could use these Cipher
objects, and we can simply reuse the `CipherFactory` instead of copy-pasting
code. Another is that in a larger project, there may be several classes that
need such an object, and we'd prefer to have a creation class that all classes
in the system can use.

The other reason isn't about code reuse at all. Software engineering has the
*dependency inversion principle*: depend on abstractions, not concretions.
After you've finished this assignment, there isn't a single line of code that
mentions concrete `Cipher` classes in `Chat.java`. Not one. Now, no matter what
we do with cipher algorithms, we aren't worried about accidentally breaking the
`Chat` class. As we make changes, we'll never have to run regression tests on
`Chat`. It's very comforting to know that you've reduced the coupling in your
system so that you aren't afraid of breaking anything important when you make a
small change to an ancillary piece of code.

## Deliverables

For grading, we will pull the `socket_chat` repositories after the assignment
is due, and we will look in the Maven-conventional directories for:

-   A properties file for your chosen language
-   Updated `socketchat_en.properties` file
-   Updated `Chat.java`
-   `Cipher.java` and `CipherFactory.java`
-   Three `Cipher` implementations (`NullCipher.java` plus two others of your
    choosing)
-   Updated `ChatTest.java`

*It is your responsibility to ensure that your work is in the master branch of
the **correct repository** and that we have the correct level of access to the
repository at the **time the assignment is due**.  We will grade what we can
retrieve from the repository at the time it is due.  Any work that is not in
the correct repository, or that we cannot access, will not be graded.*

## Rubric

The assignment is worth **25 points**:

-   **4 points** for internationalization
    -   1 points for creating the properties file for the other language
    -   3 points for writing the code to change Locales

-   **3 points** for creating the Simple Factory

-   **9 points** for the Strategy Pattern
    -   1 point for creating `Cipher.java` and `NullCipher.java`
    -   2 points each for the 2 other classical cipher algorithms
    -   1 point for delegating `encipher` and `decipher`
    -   3 points for writing the code to change cipher algorithms that is fully
        independent of `Cipher` implementations

-   **6 points** for tests
    -   2 point for testing `encipher` and `decipher` for your two classical
        cipher algorithms
    -   4 points for writing sufficient tests to attain statement coverage of
        `Chat.communicateOneMessage()`

-   **1 point** for making regular commits throughout the project

-   **2 points** for meaningful commit messages

This assignment is scoped for a team of 2 students. If, despite your attempts
to engage your partner, your partner does not contribute to the assignment then
we will take that into account when grading.

*If **at any time** your repository is public or has internal visibility then
you will receive a 10% penalty. Further, if another student accesses your
non-private repository and copies your solution then I will assume that you are
complicit in their academic dishonesty.*

## Contribution Rubric

The contribution is worth **10 points**:

-   **1 point** for completing peer assessment on time
-   **1 point** for contacting your partner promptly
-   **4 points** for equitable contribution based on peer assessments
-   **4 points** for equitable contribution based on git history

## Footnote