"Stop When We've Had Enough - Propagating the Stop Price", ch 18.

> To make this feature work, we need to pass the stop price from the user
> interface to the `AuctionSniper`, which can then use it to limit further
> bidding. The chain starts when `MainWindow` notifies its
> `UserRequestListener` using: `#join_auction(item_id)`.
>
> The obvious thing to do is to add a `stop_price` argument to this method and
> to the rest of the chain of calls, until it reaches the `AuctionSniper`
> class. We want to make a point here, so we'll force a slightly different
> approach to propagating the new value.
>
> Another way to look at it is that the user interface constructs a description
> of the user's "policy" for the Sniper's bidding on an item. So far this has
> only included the item's identifier ("bid on this item"), but now we're adding
> a stop price ("bid up to this amount on this item") so there's more structure.
>
> We want to make this structure explicit, so we create a new class, `Item`. We
> start with a simple value that just carries the identifier and stop price as
> public immutable fields; we can move behavior into it later.
>
> Introducing the `Item` class is an example of budding off that we described in
> "Value Types". It's a placeholder type that we use to identify a concept and
> that gives us somewhere to attach relevant new features as the code grows.
>
> We push Item into the code and see what breaks. First we fix
> `MainWindowTest`, the integration test we wrote for the Swing implementation
> in Chapter 16. The language is already beginning to shift. In the previous
> version of this test, the probe variable was called `button_probe`, which
> describes the structure of the user interface. That doesn’t make sense any
> more, so we've renamed it `item_probe`, which describes a collaboration
> between `MainWindow` and its neighbors.
>
> We make this test pass by extracting the stop price value within
> `MainWindow`.
"Stop When We've Had Enough - Propagating the Stop Price", ch 18.

> This pushes `Item` into `SniperLauncher` which, in turn, pushes it through to
> its dependent types such as `AuctionHouse` and `AuctionSniper`. We fix the
> compilation errors and make all the tests pass again - except for the
> outstanding end-to-end test which we have yet to implement.
>
> We've now made explicit another concept in the domain. We realize that an
> item's identifier is only one part of how a user bids in an auction. Now the
> code can tell us exactly where decisions are made about bidding choices, so
> we don't have to follow a chain of method calls to see which strings are
> relevant.
"Stop When We've Had Enough - Restraining AuctionSniper", ch 18.

> The last step to finish the task is to make the `AuctionSniper` observe the
> stop price we've just passed to it and stop bidding. In practice, we can
> ensure that we've covered everything by writing unit tests for each of the
> new state transitions. Our first test triggers the Sniper to start bidding
> and then announces a bid outside its limit - the stop price is set to 1234.
> We've also extracted a common expectation into a helper method.
>
> We change `AuctionSniper`, with supporting features in `SniperSnapshot` and
> `Item`, to make the test pass.
>
> The end-to-end tests pass and we can cross the feature off our list.
"What If It Doesn't Work?", chapter 19.

> Our product people are concerned that Southabee's On-Line has a reputation
> for occasionally failing and sending incorrectly structured messages, so they
> want us to show that we can cope. It turns out that the system we talk to is
> actually an aggregator for multiple auction feeds, so the failure of an
> individual auction does not imply that the whole system is unsafe. Our policy
> will be that when we receive a message that we cannot interpret, we will mark
> that auction as Failed and ignore any further updates, since it means we can
> no longer be sure what's happening. Once an auction has failed, we make no
> attempt to recover.
>
> In practice, reporting a message failure means that we flush the price and
> bid values, and show the status as Failed for the offending item. We also
> record the event somewhere so that we can deal with it later. We could make
> the display of the failure more obvious, for example by coloring the row, but
> we'll keep this version simple and leave any extras as an exercise for the
> reader.
>
> The end-to-end test shows that a working Sniper receives a bad message,
> displays and records the failure, and then ignores further updates from this
> auction:

      1) Error:
    test_0006_sniper_reports_invalid_auction_message_and_stops_responding_to_events(AuctionsniperSpec):
    NativeException: java.lang.AssertionError:
    Tried to look for...
        exactly 1 JTable (with name "Snipers Table")
        in exactly 1 JFrame (with name "Auction Sniper Main" and showing on screen)
        in all top level windows
    and check that it is table with row with cells <label with text "item-54321">, <label with text "0">, <label with text "0">, <label with text "failed">
    but...
        all top level windows
        contained 1 JFrame (with name "Auction Sniper Main" and showing on screen)
        contained 1 JTable (with name "Snipers Table")
       it is not table with row with cells <label with text "item-54321">, <label with text "0">, <label with text "0">, <label with text "failed">
        because
          in row 0: component 1 text was "500"
          in row 1: component 0 text was "item-65432"

> It shows that there are two rows in the table: the second is for the other
> auction, and the first is showing that the current price is 500 when it
> should have been flushed to 0. This failure is our marker for what we need to
> build next.
"Detecting the Failure", chapter 19.

> The failure will actually occur in the `AuctionMessageTranslator` which will
> throw a runtime exception when it tries to parse the message. The Smack
> library drops exceptions thrown by `MessageHandlers`, so we have to make sure
> that our handler catches everything. As we write a unit test for a failure in
> the translator, we realize that we need to report a new type of auction
> event, so we add an `#auction_failed` method to the `AuctionEventListener`
> interface.
>
> This fails with an `ArrayIndexOutOfBoundsException` when it tries to unpack a
> name/value pair from the string. We could be precise about which exceptions
> to catch but in practice it doesn't really matter here: we either parse the
> message or we don't, so to make the test pass we extract the bulk of
> `#processMessage` into a `#translate` method and wrap a try/catch block
> around it.
>
> While we're here, there's another failure mode we'd like to check. It's
> possible that a message is well-formed but incomplete. It might be missing
> one of its fields such as the event type or current price. We write a couple
> of tests to confirm that we can catch these.
>
> Our fix is to throw an exception whenever we try to get a value that has not
> been set, and we define `MissingValueException` for this purpose.

Notes:

* The "bad message" used in the first unit test results in a `NoMethodError`
  exception (with message: "undefined method `strip' for nil:NilClass") in the
  Ruby version, but I've chosen to rescue all standard errors so this is moot.
* `Hash#fetch` raises an `IndexError` with a message: "key not found", so
  I didn't bother with the `MissingValueException`.
"Displaying the Failure", chapter 19.

> We added an `#auction_failed` method to `AuctionEventListener` while
> unit-testing `AuctionMessageTranslator`. This triggers a compiler warning in
> `AuctionSniper`, so we added an empty implementation to keep going. Now it's
> time to make it work, which turns out to be easy. We write some tests in
> `AuctionSniperTest` for the new state transitions.
>
> We've added a couple more helper methods: `#ignoring_auction` says that we
> don't care what happens to auction, allowing events to pass through so we can
> get to the failure; and, `#expect_sniper_to_fail_when_it_is` describes what a
> failure should look like, including the previous state of the Sniper.
>
> All we have to do is add a `#failed` transition to `SniperSnapshot` and use
> it in the new method.
>
> The end-to-end test, however, still fails. The synchronization hook we added
> reveals that we haven't disconnected the Sniper from receiving further events
> from the auction.
>
> We turn off a Sniper by removing its `AuctionMessageTranslator` from its
> `Chat`'s set of `MessageListeners`. We can do this safely while processing a
> message because `Chat` stores its listeners in a thread-safe "copy on write"
> collection. One obvious place to do this is within `#processMessage` in
> `AuctionMessageTranslator`, which receives the `Chat` as an argument, but we
> have two doubts about this. First, as we pointed out in Chapter 12,
> constructing a real Chat is painful. Most of the mocking frameworks support
> creating a mock class, but it makes us uncomfortable because then we're
> defining a relationship with an implementation, not a role - we're being too
> precise about our dependencies. Second, we might be assigning too many
> responsibilities to `AuctionMessageTranslator`; it would have to translate
> the message and decide what to do when it fails.
>
> Our alternative approach is to attach another object to the translator that
> implements this disconnection policy, using the infrastructure we already
> have for notifying `AuctionEventListeners`.
>
> The end-to-end test, as far as it goes, passes.
"Recording the Failure", chapter 19.

> Now we want to return to the end-to-end test and the
> `#reports_invalid_message` method that we parked. Our requirement is that the
> Sniper application must log a message about these failures so that the user's
> organization can recover the situation. This means that our test should look
> for a log file and check its contents.
>
> We implement the missing check and flush the log before each test, delegating
> the management of the log file to an `AuctionLogDriver` class which uses the
> Apache Commons IO library. It also cheats slightly by resetting the log
> manager (we're not really supposed to be in the same address space), since
> deleting the log file can confuse a cached logger.
>
> This new check only reassures us that we've fed a message through the system
> and into some kind of log record - it tells us that the pieces fit together.
> We'll write a more thorough test of the contents of a log record later. The
> end-to-end test now fails because, of course, there's no log file to read.
>
> Once again, the first change is in the `AuctionMessageTranslator`. We'd like
> the record to include the auction identifier, the received message, and the
> thrown exception. The "single responsibility" principle suggests that the
> `AuctionMessageTranslator` should not be responsible for deciding how to
> report the event, so we invent a new collaborator to handle this task. We
> call it `XMPPFailureReporter`.
>
> We amend our existing failure tests, wrapping up message creation and common
> expectations in helper methods.
>
> The new reporter is a dependency for the translator, so we feed it in through
> the constructor and call it just before notifying any listeners. We know that
> `message.getBody` will not throw an exception, it's just a simple bean, so we
> can leave it outside the catch block.
>
> The unit test passes.
"Generating the Log Message", chapter 19.

> The next stage is to implement the `XMPPFailureReporter` with something that
> generates a log file. This is where we actually check the format and contents
> of a log entry. We start a class `LoggingXMPPFailureReporter` and decide to
> use Java's built-in logging framework. We could make the tests for this new
> class write and read from a real file. Instead, we decide that file access
> is sufficiently covered by the end-to-end test we've just set up, so we'll
> run everything in memory to reduce the test's dependencies. We're confident
> we can take this shortcut, because the example is so simple; for more complex
> behavior we would write some integration tests.
>
> The Java logging framework has no interfaces, so we have to be more concrete
> than we'd like. Exceptionally, we decide to use a class-based mock to
> override the relevant method in `Logger`; in jMock we turn on class-based
> mocking with the `setImposteriser()` call. The `AfterClass` annotation tells
> JUnit to call `resetLogging()` after all the tests have run to flush any
> changes we might have made to the logging environment.
>
> We pass this test with an implementation that just calls the logger with a
> string formatted from the inputs to `cannotTranslateMessage()`.
>
> Now we have the pieces in place to make the whole end-to-end test pass. We
> plug an instance of the `LoggingXMPPFailureReporter` into the
> `XMPPAuctionHouse` so that, via its `XMPPAuctions`, every
> `AuctionMessageTranslator` is constructed with the reporter. We also move
> the constant that defines the log file name there from `AuctionLogDriver`,
> and define a new `XMPPAuctionException` to gather up any failures within the
> package.
>
> The end-to-end test passes completely.
Switch Gemfile source to HTTP to avoid JRuby OpenSSL warning
Upgrade Mocha to v0.14.0.
Use matchers from Ramcrest instead of Hamcrest.

In order to avoid an unnecessary Java dependency.
Use Ruby stdlib Queue instead of Java ArrayBlockingQueue.
Use countdownlatch gem instead of Java CountDownLatch.
Tweaks to README.
Use `Item` in place of `item_id` everywhere.

This should have been done earlier when `Item` was introduced [1].

[1] "Stop When We've Had Enough - Propagating the Stop Price", ch 18.