Wednesday, May 4, 2016

Unboxing Packages: json_rpc_2

Last week I wrote about the stream_channel package for two-way communication, so this week it seemed natural to move to a package that uses it: json_rpc_2. This is an implementation of the JSON-RPC 2.0 specification, which is a popular protocol for providing structure and standardization to WebSocket APIs.

Although it’s most commonly used with WebSockets, the protocol itself is explicitly independent of the underlying transport mechanism. This makes it a great fit for stream channels, which can be used to represent a two-way stream of JSON objects in a way that works with any underlying mechanism. Thanks to stream channels, JSON-RPC 2.0 can be used across WebSockets, isolates, or any channel a user chooses to wrap.

Shared APIs

There are three main classes in json_rpc_2: Client makes requests and receives responses, Server handles requests and returns responses, and Peer does both at once. Because all of these involve two-way communication, they all have the same two constructors. The default constructor takes a StreamChannel<String> where each string is an encoded JSON object, and automatically decodes incoming objects and encodes outgoing ones. On the other hand, if you want to communicate using decoded maps and lists, you can use the withoutJson() constructor, which only requires that the objects be JSON-compatible.

The three classes also have the same lifecycle management. In order to give the user time to set up request handlers or enqueue request batches, they don’t start listening to the stream channel until listen() is called. Once it is, it returns a future that completes once the channel has closed—also accessible as the done getter. And if the user wants to close the channel themselves, they can call close().

Client

The Client class is in charge of making requests of a server. The core method for this is sendRequest(), which takes a method (the name of the remote procedure to call) and parameters to pass to that method.

The structure of these parameters depends what the server accepts. JSON-RPC 2.0 allows both positional parameters, which are passed as an Iterable of JSON-safe objects, and named ones, which are passed as a Map from string names to JSON-safe values. The parameters can also be omitted entirely if the method doesn’t take any.

The call to sendRequest() returns a future that completes with the server’s response. The protocol defines two types of response: “success” and “error”. On a success, the server returns a JSON-safe object which the sendRequest() future emits. On a failure, the server returns an error object with associated metadata. This metadata is wrapped up as an RpcException and thrown by the future.

import 'package:json_rpc_2/json_rpc_2.dart' as rpc;

/// Uses the VM service protocol to get the Dart version of a Dart process.
///
/// The [observatoryUrl] should be a `ws://` URL for the process's VM service.
Future<String> getVersion(Uri observatoryUrl) async {
  var channel = new WebSocketChannel.connect(observatoryUrl);
  var client = new rpc.Client(channel);
  client.listen();

  // getVM() returns an object with a bunch of metadata about the VM itself.
  var vm = await client.sendRequest("getVM");
  return vm["version"];
}

If you don’t care whether the request succeeds, you can also call sendNotification(). JSON-RPC 2.0 defines a notification as a request that doesn’t require a response, and a compliant server shouldn’t send one at all. Notifications are commonly used by peers for emitting events, but I’ll get to that later.

JSON-RPC 2.0 also has a notion of batches, where a bunch of requests are sent as part of the same underlying message. The server is allowed to process batched requests in whatever order it wants, but it’s required to send the responses back as a single message as well. This can use less bandwidth if you have a bunch of requests that don’t have strong ordering needs.

The json_rpc_2 client lets the user create batches using the withBatch() method. This takes a callback (which may be asynchronous), and puts all requests that are sent while that callback is running into a single batch. This batch is sent once the callback is complete.

Server

The Server class handles requests from one or more clients. Its core API is registerMethod(), which controls how those requests are handled. It just takes a method name and a callback to run when that method is called. The value returned by that callback becomes the result returned to the client.

import "package:json_rpc_2/json_rpc_2.dart" as rpc;
import "package:shelf/shelf_io.dart" as io;
import "package:shelf_web_socket/shelf_web_socket.dart";

var _i = 0;

main() async {
  io.serve(webSocketHandler((webSocketChannel) {
    var server = new rpc.Server(webSocketChannel);

    // Increments [_i] and returns its new value.
    server.handleMethod("increment", () => ++_i);
  }), 'localhost', 1234);
}

The server presents an interesting API design challenge. Most methods require certain sorts of parameters—one might need exactly three positional parameters, one might need two mandatory named and one optional parameter, and another might not allow any parameters at all. JSON-RPC 2.0 is pretty clear about how to handle this at the protocol level, but how do we let the user specify it?

We could have users manually validate the parameters—and in fact, for complex validations we do. Users can always manually throw new RpcException.invalidParams() based on whatever logic they code. But it’s a huge pain to manually validate the presence and type of every parameter, so Server uses a couple clever tricks to figure out requirements with minimal user code.

The first trick is that the callback passed to registerMethod() can take either zero or one parameters. This is how Server figures out whether the method allows parameters at all. In the example above, if a client tried to call increment with parameters of any kind, they would get an “invalid parameters” error. But the most clever trick is how parameters that are passed are parsed, and it involves an entirely new class.

Parameters

The Parameters class wraps a JSON-safe object and provides methods to access it in a type-safe way that will automatically throw RpcExceptions if the object isn’t the expected format. It’s what gets passed to the registerMethod() callback, if it takes a parameter at all.

If you call asList and the caller passed the parameters by name, it’ll throw an RpcException. If you call asMap and the parameters were passed by position? RpcException as well. Or you can just call value and get the underlying parameter no matter what form it takes.

Parameters also lets you verify the parameter values themselves. The [] operator can be used for either positional parameters (with int arguments) or named parameters (with string arguments), and returns a Parameter object which extends Parameters with a bunch of methods for validating types beyond just lists and maps.

All of the native JSON types have getters like asString, asNum, and similar. Just like asList and asMap, these getters return the parameter values if they’re the correct types and throw RpcExceptions if they aren’t. There are also derived getters like asDateTime and asUri which ensure that the value can be parsed as the appropriate type, and asInt which ensures that a number is an integer.

// Sets [_i] to the given value.
server.handleMethod("set", (parameters) {
  _i = parameters[0].asInt;
  return _i;
});

It’s important to note that the [] operator will return a parameter even if it doesn’t exist, either because there weren’t enough positional parameters passed or because a parameter with that name wasn’t passed. This makes it easy to support optional parameters.

A parameter that doesn’t exist will always throw an RpcException for its asType methods, and even for value. But there are methods where it won’t throw. If you call asStringOr() for a parameter that exists, it behaves just like asString, but for a non-existent parameter it’ll return the defaultValue parameter. Every asType getter has a corresponding asTypeOr() method. Even value has valueOr().

// Returns the logarithm of [_i].
//
// If the `"base"` named parameter is passed, uses that as the base. Otherwise,
// uses `e`.
server.handleMethod("log", (parameters) {
  return math.log(_i)/math.log(parameters["base"].asNumOr(math.E));
});

Peer

The Peer class works as both a Server and a Client over the same underlying connection. In terms of API, it’s exactly the sum of those two classes. It adds no methods of its own, so in that sense you already know everything about it. But it’s still instructive to talk about why it exists.

While I can easily imagine a structure where two endpoints are truly peers, each invoking methods on the other and receiving results, in practice most of the time I’ve seen peer-structured protocols has been for the sake of event dispatch. You see, JSON-RPC 2.0 doesn’t include an explicit mechanism for the server pushing events to the client. It can only respond to requests made by the client. This is intentional, since it makes the protocol much simpler, and the peer structure is the standard way around it.

To support server events, both the client and server must act as peers, able to send and receive requests. In this world, events are modeled as requests sent from the server to the client—or more specifically, notifications, since the server doesn’t expect a response. The client registers a method for each type of event it wants to handle, and the server sends a request for every dispatch.

/// Uses the VM service protocol to print the VM name.
///
/// Prints the VM name again every time it's changed.
void printVersions(Uri observatoryUrl) async {
  var channel = new WebSocketChannel.connect(observatoryUrl);
  var peer = new rpc.Peer(channel);

  peer.registerMethod("streamNotify", (parameters) async {
    if (parameters["streamId"].asString != "VMUpdate") {
      throw new rpc.RpcException.invalidParams(
          "Only expected VMUpdate events.");
    }

    print("VM name is ${await peer.sendRequest("getVM")["version"]}.");
  });
  client.listen();

  print("VM name is ${await client.sendRequest("getVM")["version"]}.");
}

RPC Home

Next time you need to communicate with a JSON-RPC 2.0 server, you know where to turn. Next time you need to create an RPC server, I hope you look to JSON-RPC 2.0 as the underlying protocol. It’s clean and straightforward, and best of all, it’s got a great implementation already written and ready to use.

I wrote about stream_channel in my last article. In this article, I wrote about json_rpc_2, which uses stream_channel. Join me in two weeks when I build this layer cake a little higher and write about a package that uses json_rpc_2!

Friday, April 29, 2016

Dart in Education: Interview with Prof. Dr. Nane Kratzke

Nane Kratzke is a professor of Computer Science at the Lübeck University of Applied Sciences in North Germany. He conducts cloud computing research at the university’s Center of Excellence for Communications, Systems, and Applications (CoSA).

He also gives Computer Science courses — one of which uses Dart as a vehicle to teach web programming. We asked Nane about this.

 

[Off-topic] Is it true you were researching network warfare at some point in your past?

Yes, that is true. I was enlisted in German Navy as a Navy Officer and during my military time I studied Computer Science at the University of Federal Armed Forces in Munich, Germany. For about six years after my studies, I was involved as a software engineer, team leader and project leader in several programs for command and control systems of German frigates.

After that, I worked as a consulting software architect for a German think tank consulting mainly the German Ministry of Defence. I did some research with the University of German Federal Armed Forces, Munich, concerning network-centric warfare (especially agent-based simulations) and consulted the German Ministry of Defence in questions of network-centric warfare and enterprise architecture management.

You lead a web technologies course in Lübeck. How did you design this course?

I would not say that the current course is ‘designed.’ It was more like an evolutionary process.

It started as a really standard course focusing mainly on HTML and server-side programming using PHP. Forms encoded in HTML, server-side form handling and that kind of thing. This setting is very limiting in terms of designing some fun or challenging tasks from a teaching perspective. No student ever told me that, but I am afraid this course was very boring.

I decided to let the course evolve by concentrating more on the client side using JavaScript. It turned out that focusing on the client side is much more interesting and challenging for students. It simply provides much more flexibility to create interesting tasks for students. Like developing a game for instance.

One year later Dart comes into my focus of attention. And Dart seemed to be a great language for a web technology course. I decided to give it a try.

The primary intent of the course is to let students develop something fun by applying some useful concepts of software engineering. Furthermore, the course is designed in a problem- and project-oriented way of learning and teaching. All theory lectures about web technologies is more like an introductory crash course on the very basics - just to draw the big picture. The students have to understand the web technology concepts in detail on their own, and by solving a concrete problem as a team. So, an educational theoretician would likely categorize the course as a problem-based teaching approach embedded in a project-based context (or something like that).

Games are a good combination to do this, in my opinion. Games let students forget that they are studying and that they are applying a lot of "boring stuff" like logic or modeling concepts. Notwithstanding, they learn a lot about using:

  • patterns (they get to know in their 3rd semester),
  • modeling (game logic, which is a lot of formal logic — of which students are often afraid),
  • applying object orientation (they get to know it in their 1st and 2nd semester but don’t have the chance to apply it in a more complex setting),
  • separation of concerns,
  • and so on.

If learning can be fun, it should be. This is not possible in all computer science courses of course. But in web technology, there is a clear opportunity.

Your course introduces students to HTML, CSS, DOM, HTTP, REST, ... and Dart. Why not the more obvious choice — JavaScript?

The very basic idea dates back to my own studies of computer science. In my first semester, we had to attend a programming course. Some of us could already program. Some of us not. Our professors decided to ground us all. We learned to program using "non-obvious" Haskell. None of us even heard of that language at that time (nowadays, Haskell is much more well-known, even for students).

All of us had to start at the beginner level due to that. Looking back, this was one of the best decisions. Taking a non-obvious choice of a programming language makes your students more equal. Grading is fairer because you do not grade how good a student is in a specific programming language. Instead, students get graded for their general understanding of programming and software development. Knowledge of a specific programming language does not say so much most of the times, from my experience.

How does Dart fare as a programming language for education?

I would not say that Dart fares better than any other language. Each language has its strengths and weaknesses. A preference for a programming language is a very personal point of view. I like Dart, I love Ruby (but it would be cool if Ruby had an optional type system ;-), I would like to do more with Haskell after decades of abstinence, and I am no real friend of Java (although I teach it in introductory programming courses).

But there is no objective reason for my preferences, and these are personal preferences. It is simply not worth to fight these "language wars" in my eyes. A programming language has to fulfill a purpose.

So, the main reason for me experimenting with Dart was its "non-obviousness", to ground all to the same level. Besides that, there are some further and maybe objective benefits:

  • You can use the same programming language on client and server side (which is a major advantage for web technology courses — you don’t need to introduce two or even more languages, which tends to be very time intensive).
  • You have a working dependency management system (pub). This solves a lot of nitty-gritty problems you have in a JavaScript course. Yes, there are solutions to do same in JavaScript.
  • Dart is inspired in a lot of aspects by Java and Java is our teaching language in 1st and 2nd semester.
  • Dart is a class-based, object-oriented language and therefore much more familiar to most students (compared with the prototyping style of object-oriented programming known from JavaScript).

What are some ways in which Dart could be better at this?

Of course, more tutorials would be helpful for students. Especially in the German language.

Dart has some strange concepts (from a student perspective) like inherent interfaces and a missing "protected" access modifier for methods and data fields in object-oriented programming. So Dart might be not the best choice in these details. Also, concurrent programming and isolates are not easy to use from my point of view. I would really appreciate some more pragmatic concepts for parallelism in Dart, like a parallel map for streams. I even wrote some time ago a blog post about that.

However, I am afraid there never will exist a perfect programming language. ;-)

From a teaching perspective, it is very sad that the Dart team has stopped the development of the Dart Editor. It was simply great to install an IDE with all dependencies working out of the box. Of course, the recommended IDE — WebStorm — is a great IDE as well. It provides much more features. There are good reasons that the Dart team does not want to concentrate on supporting and developing an IDE. However, for a beginner, this introduces starting obstacles. A student not known to Dart or even web programming has to install an IDE, install the Dart SDK and get the IDE configured to work with the SDK. These are no severe obstacles, but it was much easier to do the same with a one click Dart Editor install. Students were instantly ready to program Dart.

Of course, there is the online IDE — DartPad — which is a great starting point for diving into Dart. But as a teaching institution, we need an offline capable IDE installable on our machines at our labs and home on students’ laptops or desktop systems. A solution must work even if the network connection is down due to whatever reasons. It would be really great if there would be a one click installable desktop version of DartPad. I love JetBrains — the makers of WebStorm and a lot of other great IDEs. But it is never a good feeling for a teacher to wonder whether the academic and classroom licenses might be still for free in the next semester. I do not think JetBrains would drop its support for education. However, I like to have my courses autarchic and not relying on outside support and goodwill.

For those students who have had an experience with another language (but who are — presumably — still junior), what is their approach to Dart?

Our students learn to program using Java in their first and second semester. So, the transfer to Dart is quite smooth. But inherent interfaces and a missing "protected" modifier are for some students a bit hard to accept.

Some confusion arises with the optional type system at the beginning of each course. A lot of students know dynamically typed languages like PHP or JavaScript and of course, all students know statically typed languages like C or Java. Because Java is our teaching language in the first and second semester, students are used to programming in a statically typed way.

The Dart code of my students makes it evident that their programming style changes over time. They start with a lot of static types and, week by week, the types disappear more and more - almost completely. But at the end of the course types reappear in method definitions.

It is a fascinating process from a teaching perspective. This Dart style of coding is not requested for the course and is not considered for grading. It just happens by insight.

Another aspect is asynchronous programming. Because the code is still arranged in a sequence of lines it is hard to understand for many students how async methods are working at first. This feature is not as widespread in other languages. So, a lot of students can not transfer the async concept from another language to Dart. Which makes it tricky for them to understand. However, using exactly this feature to implement action handlers for DOM-tree events is astonishingly intuitive for most students. This is still a bit weird for me because students use a feature intuitively without understanding its concept. I think the reason might be, that Dart API is designed very carefully and with programmer’s intuitions in mind. But I do not know how much is intentionally designed or just API designers’ "luck." However, it works for most students.

Part of the course is applying skills by building a simple, DOM-based web game. Why a game (as opposed to, say, a TODO app)?

I think a game is a great object of investigation for a computer science student. You have a non-trivial logic, which I think is much more complex than in a TODO app. You need an elegant user interface, hiding all irrelevant complexities of the game logic. And you have to separate these kinds of concerns.

Of course, you can develop a TODO app. But no one "loves" a TODO app. Almost everybody loves gaming.

I do not think that my students would share their TODO apps. But they are proud of their games and share them with their friends, family and via social networks. I started a hall of fame for the best games of every semester in my last course because I was asked to do so by my students. They simply want to share their game outcomes.

So, students are simply much more motivated when implementing a game. I have absolutely no clue how to motivate with a TODO app.

How many students do you have each semester? What is their feedback on the course?

There are about forty to sixty students each semester (steadily increasing over the last years). We have three or four corresponding practical classes and teams of three or four students within these courses. Each team has to develop a game which is often inspired by classic arcade games like Tetris, Boulder Dash, Pac-Man and so on. The teams can choose the game concept they want to implement.

As a supervisor, I only have a look in five sprint meetings (concept, general architecture, alpha, beta, final) to assure that all games across all teams have comparable complexity and are on track.

The course is very well accepted despite the fact that developing a game is hard work, and you have to apply all of your software engineering skills to build a good game. I think the students do not see the work as intense, thanks to the fact it’s a game they’re making.

I have a habit of asking each team spontaneously in their final presentation of the game whether they could add an additional feature before release. They only have one or two days left at that time. I check that way how extendable and resilient their architecture is. It is a bit like the real world where your customer has a new idea although you were so happy to finish the project in time. The students do not have to do that. But most teams take the challenge. They are really motivated. Some of them even solved the problem at the time of the final sprint meeting. That impressed me because it shows that they not only built a game, not only solved a problem, they know how to build software in an extendable and resilient way.

What would you recommend to other educators who are thinking about teaching web technologies (or programming in general) with Dart?

Personally, I think Dart would be a great language to learn programming. But a lot of curricula in computer science depend on statically typed languages for freshman students. That might be why most study programs rely on Java, I think. So, I am afraid there is little acceptance of such an approach.

To use Dart in a web technology course is much more practical for a teacher. The simple reason is that you can work with the same language on the client and the server side. And the language is intentionally designed to support both sides. As a teacher, you do not have to introduce two or even more languages.

Especially in web technology there is a lot of previous - but heterogeneous - knowledge of technologies among all students. Some students are PHP wizards, other students are capable of programming, but have no experience in any web technology at all. This difference in skills is really challenging for a teacher. However, at the end of the course, all students should have somehow the same level of skills. I do not know any other course in a computer science study program (with the exception of introducing programming courses) where this issue is so distinctive.

A general recommendation is to keep the experienced students motivated by providing something new for them. This can be done using the "non-obvious" Dart instead of the well-known PHP/JS language combination. And let experienced students share their knowledge with the more inexperienced students. Project-based learning is a great way to do this.

However, I am afraid over the years Dart will get more and more widespread. The first students are coming in my courses and have already programmed in Dart. So, it might be that I have to find a new, non-obvious language in a few years ;-)

Tuesday, April 26, 2016

Dart 1.16: Faster tools, updated HTML APIs

Dart 1.16 is now available. This release includes important updates to our tools.

Faster developer tools


In this release, we've been working closely with our users at Google to make sure Dart is even more productive for developers. We've optimized how Dartium loads applications, improving the time it takes to open an application up to 40%. We also continue to invest in faster code analysis and quicker JavaScript compile times. You should see improved performance in this and future releases.

Updated HTML APIs


In Dart 1.15 we updated Dartium to Chrome 45 from Chrome 39. In this release, we've updated our browser APIs – dart:html, dart:svg, etc. – to align with these changes. While most of these changes involve new and lesser used APIs, you should verify your application code to find and fix possible breaks.

And more...


The SDK changelog has details about all of the updates in Dart 1.16 SDK. Get it now.

Unboxing Packages: stream_channel

The stream_channel package is the youngest I’ve written about so far—the first version was published on 28 January 2016, only three months ago as I write this! But despite its youth, it fills a very important role in the Dart ecosystem by providing a common abstraction for two-way communication.

In my article on source_span, I wrote about how important it is for a package ecosystem to provide common conventions that can be used throughout the language. stream_channel is another great example of that. The core API it provides is extremely simple, just two getters and a set of rules for them to follow, but the ability for Dart code to implement protocols independent of the underlying implementation is profound.

abstract class StreamChannel<T> {
  Stream<T> get stream;
  StreamSink<T> get sink;
}

The test package uses StreamChannel to implement a protocol for running tests that works whether the tests are in an isolate, a separate process, or even an iframe in a browser window. The web_socket_channel package uses it to define a common API for WebSockets that works the same on all platforms. And having a common API means that it’s also possible to create common utility classes in the style of the async package.

The Rules

As nice and simple as the API is, it’s important to note that there’s more to being a stream channel. A channel is logically a single entity, which means that the two APIs it exposes—its stream and its sink—have to work in concert with one another. So there are a set of rules that all valid implementations of StreamChannel must follow. These rules are designed to make it easy for users to interact with stream channels without leaving resources dangling or errors unhandled.

They also model the behavior of the dart:io WebSocket class almost exactly. Since it’s the only core library API that provides a connected stream and sink, it inspired a lot of the initial stream channel design.

Single-Subscription

The core SDK defines two types of streams. The listen() method may be called any number of times for broadcast streams, but only once for single-subscription streams. The first rule of stream channels is that the stream must be single-subscription. This is the default for streams, so most users will assume it unless stated otherwise, but it’s important to be explicit.

Stream Closure

Most of the rules have to do with the stream and/or the sink closing. This is no coincidence: the moment a channel is closed is one of the highest-risk times for logic errors, because resources can fail to be freed and events can happen in unexpected orders. The second rule addresses this: once the sink is closed, the stream closes without emitting any more events.

This means that the callback passed to stream.listen() won’t ever be called once sink.close() is called. It eliminates a whole class of potential bugs where an event could sneak in before the underlying channel was fully closed.

Sink Closure

The third rule is the inverse of the second: once the stream closes, the sink silently drops all events. This means that if your code receives an onDone event from the stream, it doesn’t need to do any extra work to avoid calling sink methods; they just automatically don’t do anything, guaranteed.

This and the stream closure rule work together to ensure that both components of the channel agree about whether it’s open or closed. Having a single canonical state makes channels more straightforward to work with and reason about, and give the user a consistent way to control and react to that state.

Subscription Canceling

The fourth rule is unusual in that it’s about a connection that shouldn’t exist between the stream and the sink. It says that canceling the stream’s subscription has no effect on the sink. This means that the only way for the channel to be closed locally is by calling sink.close()—code that’s dealing with the sink can be confident that only it (and the remote endpoint) is in charge of the channel’s connection.

Error Bouncing

The fifth rule only applies to channels that don’t have a way of transmitting arbitrary errors to the remote endpoint. If a channel can’t transmit an error, it closes and forwards the error to the sink—in particular, to the sink.done future. Errors sent to a channel that can’t handle them are probably caused by bugs in the program, and forwarding them to done makes it possible to handle them without making them look like events from the remote endpoint.

Early Closure

The sixth and final rule is less of a requirement and more of a guideline. If the stream closes before it has a listener, the sink should silently drop all events if possible, but only if possible. This is tricky because the connection may not be established at all until the stream has a listener, and if it’s not established there might be no way to tell when the stream closes. But if it is possible, this ensures that no events are sent over a channel that the user expects to be closed.

Premade Stream Channels

Even in the few short months that stream_channel has existed, there are already a few classes that implement the interface to wrap commonly-used two-way communication channels.

IsolateChannel

The IsolateChannel class is part of the stream_channel package. Its default constructor, new IsolateChannel(), simply wraps an existing ReceivePort and a SendPort in a stream channel.

That’s great if you already have both ports, but what if you’re establishing the initial connection? Anyone who’s written a bunch of isolate code knows what a pain it is to correctly do the dance of sending a port that sends back another port to establish a two-way connection. To make that easier, IsolateChannel provides two utility constructors. new IsolateChannel.connectReceive() takes a ReceivePort, and new IsolateChannel.connectSend() takes the attached SendPort. They then use an internal protocol to connect ports going the other direction so you don’t have to worry about it.

/// Spawns a worker isolate and returns a [StreamChannel] for communicating with
/// it.
Future<StreamChannel> spawnWorker() async {
  var port = new ReceivePort();
  var isolate = await Isolate.spawn(worker, port.sendPort);
  return new IsolateChannel.connectReceive(port);
}

/// The entrypoint for the worker isolate.
void worker(SendPort port) {
  var channel = new IsolateChannel.connectSend(port);
  /// ...
}

WebSocketChannel

This isn’t strictly part of the stream_channel package, but the WebSocketChannel class defined in the web_socket_channel package is another great example of a stream channel. It has two concrete implementations: IOWebSocketChannel wraps the WebSocket class from dart:io, whereas HtmlWebSocketChannel wraps the one from dart:html.

Both implementations have one constructor that wraps the underlying class, as well as another that opens a new connection. Otherwise, they provide pretty much the same API (with one platform-specific getter).

WebSocketChannel is particularly interesting because it’s not just a vanilla StreamChannel. It provides a few additional APIs: protocol includes the meta-protocol if one was negotiated, and if the socket is closed by the remote end point closeCode and closeReason indicate why. Its sink is also a custom subclass whose close() method allows the user to specify their own code and reason.

Creating New Stream Channels

If you need a stream channel for a different kind of underlying communication channel, you may need to create your own. You could just call new StreamChannel() with a stream and a sink, but be careful: if your stream and sink don’t satisfy the rules I described above, you’re liable to run into some really tricky bugs.

The new StreamChannel.withGuarantees() constructor is much safer, at the cost of providing some extra layers of wrapping. It ensures that, regardless of the behavior of the stream and sink that are passed in, the ones exposed by the channel satisfy all the rules.

StreamChannelController

If you don’t have a preexisting stream and sink, you can create a channel from scratch using the StreamChannelController class. This exposes two stream channels. The code managing the controller interacts directly with the local channel. This is connected to the foreign channel, which is meant to be returned to be used by external code.

/// Returns a [StreamChannel] that communicates over [port].
StreamChannel messagePortChannel(MessagePort port) {
  var controller = new StreamChannelController(allowForeignErrors: false);

  // Pipe all events from the message port into the local sink...
  port.listen((message) => controller.local.sink.add(message.data));

  // ...and all events from the local stream into the send port.
  controller.local.stream.listen(port.postMessage, onDone: port.close);

  // Then return the foreign controller for your users to use.
  return controller.foreign;
}

StreamChannelController automatically ensures that the stream channel rules are satisfied for both the local and remote channels. The allowForeignErrors parameter to the constructor controls how error bouncing is handled. By default, errors are passed straight from the foreign channel to the local one. But if there’s no way to deal with those errors, allowForeignErrors: false can be passed to forward those errors to foreign.sink.done instead.

MultiChannel

I’ll finish this article by talking about one of the coolest stream channel utility classes. MultiChannel allows multiple independent virtual channels to communicate over a single underlying channel—it’s similar to having multiple SendPorts all communicating with different parts of the same isolate, but it works across any channel at all.

The MultiChannel is itself a stream channel, and it’s usually used to establish the initial connection. But the most important part of the class is its virtual channels, which are created using the virtualChannel() method. Each VirtualChannel provides an opaque id that the remote endpoint can pass to virtualChannel() to create its own virtual channel connected to the local one.

/// Serializes [test] into a JSON-safe map.
Map serializeTest(MultiChannel channel, Test test) {
  // Create a virtual channel for the test so that the remote endpoint can tell
  // us to run it.
  var testChannel = channel.virtualChannel();
  testChannel.stream.listen((message) async {
    assert(message['command'] == 'run');
    testChannel.add({"result": await test.run()});
  });

  return {
    "type": "test",
    "name": test.name,
    "channel": testChannel.id
  };
}

/// Deserializes [test] into a concrete [Test] class.
Test deserializeTest(MultiChannel channel, Map test) {
  // Create a virtual channel connected to the one created in [serializeTest].
  var testChannel = channel.virtualChannel(test['channel']);
  return new Test(test['name'], testChannel);
}

The underlying stream is only closed once the initial MultiChannel and all virtual channels are closed. This lets the channels remain fully independent, but it also means it’s important to be scrupulous about closing them when their job is done.

Sink or Stream

This isn’t quite everything in the stream_channel package, but it’s all the most important parts. You’ll just have to check out the API docs for the rest! And next time you need two-way communication, you know where to look.

Join me next week when I talk about a package that’s built on top of stream_channel.

Tuesday, April 5, 2016

Unboxing Packages: async Part 3

We’ve covered individual values and we’ve covered streams, but there are still a few more goodies available in the async package. These don’t fit neatly in either bucket, but when you run into situations that call for them, they’re still plenty useful.

Wrappers

Just like the collection package, async provides a set of wrapper classes. Each of these classes implements a dart:async class and forwards all calls to an inner instance of that class. This makes it easy for users to provide customized versions of those classes. In fact, the async package itself uses some of its own wrappers.

There’s a DelegatingFuture class, of course. There’s also a DelegatingStreamSubscription, and wrappers for every kind of sink or consumer you can name: DelegatingSink, DelegatingEventSink, DelegatingStreamConsumer, and of course DelegatingStreamSink. Of these, DelegatingStreamSink is used most often since it encompasses all the functionality of the other classes.

/// A [StreamSink] that takes extra arguments for [close].
class WebSocketSink extends DelegatingStreamSink {
  final WebSocket _webSocket;

  WebSocketSink(WebSocket webSocket)
      : super(webSocket),
        _webSocket = webSocket;

  /// Closes the web socket connection with the given [code] and [reason].
  Future close([int code, String reason]) => _webSocket.close(code, reason);
}

You may notice that there’s no DelegatingStream. That’s because it already exists in the core libraries! It’s called StreamView, and it’s in dart:async. Its presence in the core libraries and its inconsistent name are historical accidents, and at some point we may add a DelegatingStream that just directly extends StreamView for consistency.

RestartableTimer

The RestartableTimer class is very small, but despite that I think it’s useful enough to warrant a mention here. It’s a subclass of Timer, and new RestartableTimer(), isActive, and cancel() all work just the same as the superclass.

The big difference is the all-new reset() method. This restarts the timer, with its original timer and callback. It’ll start counting down again whether or not it already fired, which means that the callback can be called more than once.

This is really useful for implementing a heartbeat—for example, in test, we use a RestartableTimer to time out a test if it doesn’t interact with the test framework for too long.

class Invoker {
  RestartableTimer _timeoutTimer;

  Invoker(...) {
    _timeoutTimer = new RestartableTimer(new Duration(seconds: 30), () {
      registerException("The test timed out.");
    });
  }

  // Functions like `expect()` call this to notify the test runner that the test
  // is still doing work. If it's not called for thirty seconds, the test times
  // out.
  void heartbeat() {
    _timeoutTimer.reset();
  }
}

Stream Sink Utilities

Conceptually, streams have two ends: the Stream object is where events are emitted, and it’s where most of the complexity lies, but for every Stream there’s a StreamSink behind the scenes feeding it events. If your code is the one in charge of that sink, you may need utilities for dealing with them, and the async package is ready to serve.

StreamSinkTransformer

The core dart:async library includes a StreamTransformer class, which provides an abstraction for transforming streams in a predefined way. The core libraries use it for converters for formats like JSON and UTF-8. But there’s no core library class for transforming StreamSinks, so async steps in to pick up the slack.

The StreamSinkTransformer class works almost exactly like StreamTransformer: you pass in a sink to bind(), and it returns a new sink that’s transformed according to the transformer’s internal logic. It also has a new StreamSinkTransformer.fromHandlers() transformer that’s just like new StreamTransformer.fromHandlers(): it invokes the handlers when the corresponding events are added to the transformed sink.

The one novel API is the new StreamSinkTransformer.fromStreamTransformer() constructor. This transforms a StreamTransformer into a corresponding SinkTransformer, which lets you take advantage of all the useful transformers in the core libraries.

main() async {
  // WebSocket implements both Stream and StreamSink. We want to communicate
  // using JSON, so we decode the streamed events and encode the events added to
  // the sink.
  var socket = await WebSocket.connect("ws://example.com");
  var stream = socket.transform(JSON.decoder);
  var sink = const StreamSinkTransformer.fromStreamTransformer(JSON.encoder)
      .bind(socket);

  // ...
}

StreamSinkCompleter

This complements a class, not from the core libraries, but from the async package itself. In my last article, I talked about StreamCompleter, which allows you to return a stream immediately when that stream is only actually generated later on. StreamSinkCompleter performs the same role, but for sinks.

The API is pretty much entirely parallel to StreamCompleter: the sink getter returns the sink that’s being completed, and the setDestinationSink() method sets the underlying implementation of that sink. There’s even a StreamSink.fromFuture() method that directly converts a Future<StreamSink> into a StreamSink.

There is a bit of extra subtlety with the setError() method, though. Sinks don’t emit events the same way streams do, but they do have a way of surfacing errors: the done future. So when you call setError(), it causes the sink to ignore all events that are added and emit the given error through done.

StreamQueue

StreamQueue may be my favorite of all the APIs in the async package. It straddles the boundary between streams and individual values by taking a stream—which operates by pushing events to callbacks—and exposing a pull-based API structured mostly around futures.

The simplest example is the next getter, which returns a future that completes to the next value in the stream. So the first time you call it, it returns the first event. Then it’ll return the second event, then the third, and so on. This is all true even if you call it multiple times before the first future completes—StreamQueue keeps track of which future events belong to which futures.

void main() async {
  var queue = new StreamQueue(new Stream.fromIterable([1, 2, 3]));
  var first = queue.next;
  var second = queue.next;
  var third = queue.next;
  print(await Future.wait([first, second, third])); // => [1, 2, 3]
}

If you call next and there turn out to be no events left in the stream, it’ll complete with a StateError. If you’re worried about this, you can check hasNext first to see if such a value exists.

There’s a take() method, which consumes a given number of events and returns a list containing them—or just all the remaining values, if there are fewer than the given number. There’s also skip(), which is like take() except that it ignores the values entirely.

If you have a StreamQueue and you want a stream instead, you can use the rest getter. This returns the portion of the underlying stream after all the reserved events. We can use it to re-write the firstAndRest() function from my last article:

/// Calls [onFirst] with the first event emitted by [source].
///
/// Returns a stream that emits all events in [source] after the first.
Stream firstAndRest(Stream source, void onFirst(value)) {
  var queue = new StreamQueue(source);
  queue.next.then(onFirst);
  return queue.rest;
}

Finally, when you’re done with a queue, you can call cancel(). This cancels the subscription to the underlying stream, but by default, it waits to do so until after the stream fires enough events to complete all outstanding futures from next and friends. However, if you pass immediate: true, it’ll cancel the subscription right away instead. The outstanding futures will complete as though the stream had closed.

StreamQueue is particularly useful for testing streams. It lets you write very straightforward code that just tests each event in series, rather than fiddling around explicit calls to Stream.listen(). For example, here’s a real test for CancelableOperation:

void main() {
  test("asStream() emits an error and then closes", () async {
    var completer = new CancelableCompleter();
    var queue = new StreamQueue(completer.operation.asStream());
    expect(queue.next, throwsA("error"));
    expect(queue.hasNext, isFalse);
    completer.completeError("error");
  });
}

In addition to writing great blog posts about cool packages, I maintain the test package, and one of my long-term plans is to introduce a set of stream matchers. These matchers will be heavily based on StreamQueue, since it’s so handy for moving through a stream event by event.

isEmpty

Three articles later, and I’m finally done showing off everything cool in the async package—at least until more cool stuff gets added! Master these tools and you’ll be well on your way to being an asynchrony expert. Maybe you’ll even find some unfilled needs, work up a solution, and send us a pull request!

Come back again in two weeks when I talk about a package that builds a brand new abstraction on top of async.

Thursday, March 31, 2016

The new AdWords UI uses Dart — we asked why

Google just announced a re-designed AdWords experience. In case you’re not familiar with AdWords: businesses use it to advertise on google.com and partner websites. Advertising makes up majority of Google’s revenue, so when Google decides to completely redo the customer-facing front end to it, it’s a big deal.

The Dart team is proud to say that this new front end is built with Dart and Angular 2. Whenever you asked us whether Google is ‘even using Dart for anything,’ this is what we had in mind but couldn’t say aloud. Until now.

'This is built with Dart' - and an arrow pointing to screenshots of the new AdWords UI.

We asked Joshy Joseph, the primary technical lead on the project, some questions. Joshy is focusing on things like infrastructure, application latency and development velocity, so he’s the right person to ask about Dart.

 

Q: What exactly did we launch on Monday?

It’s a complete redesign of the AdWords customer experience that is rolling out slowly as a test to a small initial set of advertisers. The most noticeable thing is probably the Material Design look and feel. But there are many underlying improvements to the user experience. Read the Inside AdWords post for more information.

Q: How big is the team building this and how big is the project?

Can’t give specific details but the team has dozens of engineers, and the codebase is in the hundreds of thousands of lines of code.

Q: What's the tech stack?

The new AdWords advertiser UI is built as a collection of large single page applications integrated together in the browser. The browser side stack is based on Dart and Angular2 for Dart. We have some infrastructure built on top of these that we share with many other apps at Google.

Q: How is that different from what the stack your team used previously (for the current version of AdWords)?

The present version of AdWords uses a stack based on GWT. The core foundation of the stack is from about eight years ago.

Q: Why even change the tech stack? Didn't it work?

GWT actually worked very well for us. Eight years ago, different browsers worked very differently. We needed to support IE6 for instance, and that was a big challenge because we were building one of the largest single page applications at the time. Chrome hadn’t even launched yet. GWT gave us an abstraction layer across the different browsers. It also gave the language and tools support to scale to very large codebases and teams.

On the other hand, many things have changed over the last several years with respect to browser technology and UI infrastructure. Browsers have become a lot more consistent in implementing standard APIs. Also, new UI development frameworks like Angular have become very popular. There are new emerging standards like web components that we would like to take advantage of. We also have new languages like Dart that are specifically designed to transpile well to Javascript.

So a couple of years ago, when we were starting to think about building a new version of AdWords with much improved UI and performance, we took a fresh look at the tech stack. We wanted to use the opportunity to upgrade to a modern infrastructure that will serve us well for the next 7+ years.

Q: Can you describe the decision process that had led to using Dart?

Since updating the technology stack that is used for the entire AdWords UI is a huge deal, this has been a multi-year process where we first implemented portions of current AdWords using Dart and Angular. Using this stack, we successfully built a very large internal application before deciding to use it for the new AdWords UI.

Q: Why not Closure lib? GWT? TypeScript? Vanilla JS?

Those are all options we considered very carefully since — given the scale of AdWords — we can’t switch tech stacks easily. We wanted to use a stack that will enable building very large mission critical applications such as AdWords with very good user experience, application latency and feature velocity.

We wanted to provide a lot of flexibility to our UX designers to innovate and build a visually appealing and productive UI.

We also wanted to have world class application latency. A lot of people stay logged into AdWords all day working with large amounts of data. So, having a very fast application is critical.

At the same time, AdWords team as whole is constantly innovating and launching several new features every week. We wanted to not only maintain that velocity but make it even better.

Meeting all these very high bars for user experience, latency and feature velocity at the same time in a very large mission critical application is very hard. We thought Dart and Angular together was a good foundation to build the additional infrastructure we wanted to build for achieving all these goals.

Q: Were your assumptions about switching to Dart proved correct? Was something better or worse than you expected?

Being able to scale to large teams and code bases have been true. Also, having Angular available for Dart has been great. We are also doing fairly well on being on track to achieve our UX, latency and feature velocity goals. These are all areas we continue to work hard on making things even better over the coming months.

One thing we found out was that developers preferred even stronger type checking than what Dart was providing. So, we are very excited about the work on Dart Strong Mode. We are also looking forward to cross-browser, fast edit refresh with the upcoming Dart Dev Compiler. The new js interop is also a big improvement. We also realized that we can’t really use mirrors in production apps and have been avoiding doing that.

Q: How did developers on the team react to the decision to use Dart?

The decision was primarily driven by the engineers on the team. We actually had a very formal evaluation at some point after we had some experience with the stack and a small team of senior engineers considered various options across more than 50 key requirements and unanimously recommended Dart.

Q: How long did they take to learn the new language?

It is typically in the order of a couple of weeks. Since Dart looks familiar to many of the popular languages, developers tend to pick it up pretty fast.

Q: Is it hard to get Dart developers?

In general, staffing up the team hasn’t been a problem. Most engineers join AdWords because they love working on a high impact product, working with large data sets and solving complex problems. Having a state of the art tech stack for building large scale business apps helps as well.

There are some engineers though who have a strong preference for a language and make project decisions based on language. So, we have actually seen some engineers joining the team because they love Dart. There are also probably a few engineers who went to other projects because they loved the language that project was using.

Q: What is your favorite feature of Dart?

I really like how Dart is terse. Angular is also terse. So, with the help of these and other infrastructure we built on top of these, our hope is that the new AdWords UI can be implemented with less than 50% of the lines of code as the previous version while having a similar or larger set of features.

Q: What's missing with Dart?

I think the cross-browser story for fast edit refresh is the biggest gap. It is being addressed with the upcoming Dart Dev Compiler though.

Q: Is there a type of project, software or team to which you'd especially recommend Dart?

With Angular2, the application performance and code size are very similar between the JS and Dart versions. So any team considering Angular2 should consider Dart as well.

I think Dart and Angular2/Dart are especially suitable for large scale business web applications. The tooling support and static correctness checking are particularly valuable for those types of applications.

With the ongoing work on Flutter, Dart could become a good option to consider also for teams that need to build native mobile apps across Android and iOS.


Interested in Dart? Awesome. You can learn about the ideas behind Dart, or you can get your feet wet directly.