# 6.005 Recitation 6
- PS3 beta is due **tomorrow** at 5pm
- PS3 code reviews are due this Sunday, final is due Wednesday at 5pm
- PS4 will be released next week
- Only three more lectures before **Quiz 2**
Links for this recitation:
- **[Google Form][form]** for submitting your work in pairs
- **[The starting code][code]** — import this project into Eclipse
(the code will include example solutions posted later today or tomorrow)
[form]: https://docs.google.com/forms/d/1mkh8TieuizNd5mGVxnokkVpG4xKN6dlxXlQ7xLooWZU/viewform
[code]: https://github.com/mit6005/S13-R06
## Software in 6.005
Safe from bugs | Easy to understand | Ready for change |
Correct today and correct in the unknown future.
|
Communicating clearly with future programmers, including future you.
|
Designed to accommodate change without rewriting.
|
## Today: producer—consumer and publish—subscribe
The goal of this recitation is to give you more experience with two important patterns: *producer—consumer*, where components exchange objects on a *queue*; and *publish—subscribe*, where components exchange objects representing *events*.
### Setup
Today we'll be working in pairs — on some code.
Create an Eclipse project with the starting code:
- With git: `git clone https://github.com/mit6005/S13-R06.git r06`
- Without git: [download a zip file](https://github.com/mit6005/S13-R06/archive/master.zip)
- Import the project into Eclipse using *File → Import... → General → Existing Projects into Workspace*
### Concurrent Queues
What is a [`Queue`](http://docs.oracle.com/javase/6/docs/api/java/util/Queue.html)?
- FIFO (first-in-first-out)
- `add` vs. `offer`
- `remove` vs. `poll`
- `element` vs. `peek`
And what is a [`BlockingQueue`][BlockingQueue]?
- `put(E)` and `offer(E, long, TimeUnit)` vs. `add(E)`/`offer(E)`
- `take()` and `poll(long, TimeUnit)` vs. `remove()`/`poll()`
The [Java API documentation for `BlockingQueue`][BlockingQueue] mentions several useful details:
- thread safety
- poison pills
- a code template for producer-consumer
[BlockingQueue]: http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/BlockingQueue.html
Note that Java also provides several other queue variants, including priority queues, `Deque`s and `BlockingDeque`s.
### Implementing "grep" using producer—consumer
[`grep`](http://en.wikipedia.org/wiki/Grep) is a command-line tool that searches for matches to a regular expression in a set of files.
For example, in the project directory for this recitation, we can search for lines of Java code that call a constructor and then call a method on the same line:
$ grep 'new .*).*\.' src/*/*.java
src/github/HubGUI.java: new HubGUI(pub).setVisible(true);
src/github/Hubbub.java: new Thread(pub).start();
src/github/Hubbub.java: avatar = new URL(obj.getJSONObject("actor").getString("avatar_url"));
We're going to implement a version of grep that searches the HTML source code of web pages for matches to a regular expression.
We'll implement a **`Producer`** that (1) downloads the text of a page and (2) adds each line of text to queue.
And we'll implement a **`Consumer`** that (1) retrieves lines of text from the queue, (2) checks each line for a match to the regular expression, and (3) adds matching lines to a result list.
Our grep implementation will use *multiple producers* — one per URL — and *multiple consumers*.
#### Step 1
You'll work on the code in a pair: your TA will assign you either `Producer` or `Consumer`.
Implement your component of the grep program.
- What does your component need to do?
- What parameters do you need to pass in to the constructor?
Stuck trying to read the contents of a URL?
BufferedReader in = new BufferedReader(new InputStreamReader(new URL(url).openStream()));
String line;
while ((line = in.readLine()) != null) { Do something with line }
#### Step 2
Exchange `Producer` and `Consumer` implementations with another pair.
Integrate their code and complete the program by implementing `main`.
- How can we tell the consumers to stop waiting for new lines after the producers are done?
Stuck trying to tell the consumers to stop?
Try creating poison pills: special elements on the queue that signal a consumer to end its work:
class Consumer implements Runnable {
// ...
public void run() {
// ...
while (true) {
Line line = take a line off the queue
if (line is a poison pill) { break; }
continue with normal processing
}
}
}
#### Discuss
Look at the staff solution and compare with your own.
- When do producer threads stop?
- When do consumer threads stop?
- What objects are shared by multiple threads?
Why is the sharing safe?
Why are we safe from deadlocks?
- How could we stop consumer threads without using poison pills?
### Events
Especially as you start writing graphical user interfaces in Swing, you will need to understand [events](http://docs.oracle.com/javase/tutorial/uiswing/events/index.html) and event-based programming.
In almost any system with user interaction, user actions generate *events* which are delivered to components that have subscribed to hear about them: mouse clicks, key presses, drag-and-drops.
More generally, you will often encounter events and the *publish-subscribe* pattern when we want to decouple components that generate data (or operations) from components that act on those data, and when we want a flexible relationship between publishers and subscribers that can change at runtime.
### Implementing a GitHub notifier with publish—subscribe
[GitHub](https://github.com) is a popular Git hosting service where you can store your own code, and find & contribute to open-source and free-software projects.
Let's implement a widget that notifies us of public activity on the [GitHub timeline](https://github.com/timeline) — new repositories, pushes, ticket-tracker issues, etc.
#### Step 1
First, we'll implement a console view.
With your partner, complete `HubPublisher` in `Hubbub.java` so that new listeners are notified of GitHub events.
After you have listeners implemented, running `main` in `Hubbub` should start to output events on the console:
IssueCommentEvent:twitter/bootstrap
PullRequestEvent:alohaeditor/Aloha-Editor
PushEvent:fesplugas/typus
WatchEvent:ether/etherpad-lite
PushEvent:ginatrapani/ThinkUp
Be sure to hit the stop button!
Discuss the publisher implementation.
- How can we make our listener list thread-safe?
#### Step 2
Now, let's wire up the GUI.
Uncomment the line in `Hubbub.java` that creates a `HubGUI`.
Finish the incomplete `HubGUI.subscribe` and `Widget.event` methods in `HubGUI.java`.
**Is it safe to manipulate Swing components directly in `Widget.event`? No!**
Use [`SwingUtilities.invokeLater`](http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingUtilities.html#invokeLater%28java.lang.Runnable%29) to run code on the Swing event thread.
Now when we run the program, we should be able to add an event type (e.g. "PushEvent" or "WatchEvent") by typing in the text box and pressing *enter*.
The example code uses [`GroupLayout`](http://docs.oracle.com/javase/6/docs/api/javax/swing/GroupLayout.html) to control how Swing components are laid out on the screen.
Each component must be constrained both horizontally and vertically; the combination of all constraints determines the overall layout.
Consult the Java API documentation for details and for documentation on [other layout managers](http://docs.oracle.com/javase/6/docs/api/java/awt/LayoutManager2.html) you can choose from.
#### Discuss
Look at the staff solution and compare with your own.
- What thread does the `HubGUI` constructor run on? Why?
- What thread does the `Widget.event` method run on? Why?
- What happens if we enter an invalid event type?
[`JOptionPane`](http://docs.oracle.com/javase/6/docs/api/javax/swing/JOptionPane.html) has a number of useful methods for throwing up dialog boxes.
- What happens if we enter the same event type twice?
What does the listener list look like as we add more event types?
Notice how we can have console output and the GUI working at the same time — without the `HubProducer` knowing which concrete `HubListeners` are attached!
## The right communication pattern can make your code simpler and safer
**Thread-safe queues** allow you to collect data or operations from one or more *producer* threads and execute them on a single *consumer* thread — or multiple *consumers* — safely, with less shared data that must be made safe for concurrency.
**Events** allow *publishers* to notify *subscribers* (frequently called *listeners* or *observers*) about new data or operations without having to know their concrete type, and it allows for dynamic changes to which subscribers are receiving events from which publishers.
**Publish-subscribe** is an important part of the **model-view-controller** pattern which is used in some form by almost every user-facing software framework ([desktop][swing] [and][cocoa] [mobile][ios] [GUIs][android], [modern][backbone] [web][rails] [applications][django], ...).
[swing]: http://docs.oracle.com/javase/tutorial/uiswing/index.html
[cocoa]: https://developer.apple.com/library/mac/documentation/General/Conceptual/CocoaEncyclopedia/Model-View-Controller/Model-View-Controller.html
[ios]: https://developer.apple.com/library/ios/referencelibrary/GettingStarted/RoadMapiOS/chapters/KnowtheCoreObjectsofYourApp/KnowCoreAppObjects/KnowCoreAppObjects.html
[android]: http://developer.android.com/guide/components/activities.html
[backbone]: http://backbonejs.org
[rails]: http://guides.rubyonrails.org
[django]: https://docs.djangoproject.com
Neither of these patterns is without downsides: importantly, both make the control flow of your system harder to follow.
And it should come as no surprise that these patterns are often used together.
You will need to contend with the Swing event queue, managing concurrent user operations, and building a responsive GUI in Problem Set 4.
----