move code to src/test

* so that it compiles and tests pass
* fix some additional snip references in getting started
This commit is contained in:
Patrik Nordwall 2017-05-11 15:11:25 +02:00
parent 413df8e0f4
commit 59f53e1a22
289 changed files with 45 additions and 45 deletions

View file

@ -459,7 +459,7 @@ deterministically in the serialization.
This is a protobuf representation of the above `TwoPhaseSet`:
@@snip [TwoPhaseSetMessages.proto]($code$/protobuf/TwoPhaseSetMessages.proto) { #twophaseset }
@@snip [TwoPhaseSetMessages.proto]($code$/../main/protobuf/TwoPhaseSetMessages.proto) { #twophaseset }
The serializer for the `TwoPhaseSet`:
@ -481,7 +481,7 @@ The two embedded `GSet` can be serialized as illustrated above, but in general w
new data types from the existing built in types it is better to make use of the existing
serializer for those types. This can be done by declaring those as bytes fields in protobuf:
@@snip [TwoPhaseSetMessages.proto]($code$/protobuf/TwoPhaseSetMessages.proto) { #twophaseset2 }
@@snip [TwoPhaseSetMessages.proto]($code$/../main/protobuf/TwoPhaseSetMessages.proto) { #twophaseset2 }
and use the methods `otherMessageToProto` and `otherMessageFromBinary` that are provided
by the `SerializationSupport` trait to serialize and deserialize the `GSet` instances. This

View file

@ -204,7 +204,7 @@ Next we prepare an protocol definition using the protobuf Interface Description
the serializer code to be used on the Akka Serialization layer (notice that the schema aproach allows us to easily rename
fields, as long as the numeric identifiers of the fields do not change):
@@snip [FlightAppModels.proto]($code$/protobuf/FlightAppModels.proto) { #protobuf-read-optional-proto }
@@snip [FlightAppModels.proto]($code$/../main/protobuf/FlightAppModels.proto) { #protobuf-read-optional-proto }
The serializer implementation uses the protobuf generated classes to marshall the payloads.
Optional fields can be handled explicitly or missing values by calling the `has...` methods on the protobuf object,

View file

@ -471,7 +471,7 @@ deterministically in the serialization.
This is a protobuf representation of the above `TwoPhaseSet`:
@@snip [TwoPhaseSetMessages.proto]($code$/protobuf/TwoPhaseSetMessages.proto) { #twophaseset }
@@snip [TwoPhaseSetMessages.proto]($code$/../main/protobuf/TwoPhaseSetMessages.proto) { #twophaseset }
The serializer for the `TwoPhaseSet`:
@ -493,7 +493,7 @@ The two embedded `GSet` can be serialized as illustrated above, but in general w
new data types from the existing built in types it is better to make use of the existing
serializer for those types. This can be done by declaring those as bytes fields in protobuf:
@@snip [TwoPhaseSetMessages.proto]($code$/protobuf/TwoPhaseSetMessages.proto) { #twophaseset2 }
@@snip [TwoPhaseSetMessages.proto]($code$/../main/protobuf/TwoPhaseSetMessages.proto) { #twophaseset2 }
and use the methods `otherMessageToProto` and `otherMessageFromBinary` that are provided
by the `SerializationSupport` trait to serialize and deserialize the `GSet` instances. This

View file

@ -111,14 +111,14 @@ The usual pattern is to have your system set up to stop on external signal (i.e.
Once there is an `ActorSystem` we can populate it with actors. This is done by using the `actorOf` method. The `actorOf` method expects a `Props` instance and the name of the actor to be created. You can think of the `Props` as a configuration value for what actor to create and how it should be created. Creating an actor with the `actorOf` method will return an `ActorRef` instance. Think of the `ActorRef` as a unique address with which it is possible to message the actor instance. The `ActorRef` object contains a few methods with which you can send messages to the actor instance. One of them is called `tell`, or in the Scala case simply `!` (bang), and this method is used in the example here below. Calling the `!` method is an asynchronous operation and it instructs Akka to send a message to the actor instance that is uniquely identified by the actor reference.
@@snip [HelloWorldApp.scala](../../../../test/scala/quickstart/HelloWorldApp.scala) { #create-send }
@@snip [HelloWorldApp.scala]($code$/scala/quickstart/HelloWorldApp.scala) { #create-send }
Before we can create any actor in the actor system we must define one first. Luckily, creating actors in Akka is quite simple! Just have your actor class extend `akka.actor.Actor` and override the method `receive: Receive` and you are good to go. As for our `HelloWorldActor` class, it extends `Actor` and overrides the `receive` method as per the requirement. Our implementation of the `receive` method expects messages of type `String`. For every `String` message it receives it will print "Hello " and the value of the `String`. Since the message we send in the main class is "World" we expect the string "Hello World" to be printed when running the application.
@@snip [HelloWorldApp.scala](../../../../test/scala/quickstart/HelloWorldApp.scala) { #actor-impl }
@@snip [HelloWorldApp.scala]($code$/scala/quickstart/HelloWorldApp.scala) { #actor-impl }
Here is the full example:
@@snip [HelloWorldApp.scala](../../../../test/scala/quickstart/HelloWorldApp.scala) { #full-example }
@@snip [HelloWorldApp.scala]($code$/scala/quickstart/HelloWorldApp.scala) { #full-example }
Now that you have seen the basics of an Akka application it is time to dive deeper.

View file

@ -78,7 +78,7 @@ convenient terminology, and we will stick to it.
Creating a non-top-level actor is possible from any actor, by invoking `context.actorOf()` which has the exact same
signature as its top-level counterpart. This is how it looks like in practice:
@@snip [Hello.scala](../../../../test/scala/tutorial_1/ActorHierarchyExperiments.scala) { #print-refs }
@@snip [Hello.scala]($code$/scala/tutorial_1/ActorHierarchyExperiments.scala) { #print-refs }
We see that the following two lines are printed
@ -126,7 +126,7 @@ The actor API exposes many lifecycle hooks that the actor implementation can ove
Again, we can try out all this with a simple experiment:
@@snip [Hello.scala](../../../../test/scala/tutorial_1/ActorHierarchyExperiments.scala) { #start-stop }
@@snip [Hello.scala]($code$/scala/tutorial_1/ActorHierarchyExperiments.scala) { #start-stop }
After running it, we get the output
@ -151,7 +151,7 @@ to the parent, which decides how to handle the exception caused by the child act
stop and restart the child. If you don't change the default strategy all failures result in a restart. We won't change
the default strategy in this simple experiment:
@@snip [Hello.scala](../../../../test/scala/tutorial_1/ActorHierarchyExperiments.scala) { #supervise }
@@snip [Hello.scala]($code$/scala/tutorial_1/ActorHierarchyExperiments.scala) { #supervise }
After running the snippet, we see the following output on the console:
@ -211,11 +211,11 @@ The first actor happens to be rather simple now, as we have not implemented any
is that we have dropped using `println()` and instead use the `ActorLogging` helper trait which allows us to use the
logging facility built into Akka directly. Furthermore, we are using a recommended creational pattern for actors; define a `props()` method in the [companion object](http://docs.scala-lang.org/tutorials/tour/singleton-objects.html#companions) of the actor:
@@snip [Hello.scala](../../../../test/scala/tutorial_1/IotSupervisor.scala) { #iot-supervisor }
@@snip [Hello.scala]($code$/scala/tutorial_1/IotSupervisor.scala) { #iot-supervisor }
All we need now is to tie this up with a class with the `main` entry point:
@@snip [Hello.scala](../../../../test/scala/tutorial_1/IotApp.scala) { #iot-app }
@@snip [Hello.scala]($code$/scala/tutorial_1/IotApp.scala) { #iot-app }
This application does very little for now, but we have the first actor in place and we are ready to extend it further.

View file

@ -29,7 +29,7 @@ The protocol for obtaining the current temperature from the device actor is rath
We need two messages, one for the request, and one for the reply. A first attempt could look like this:
@@snip [Hello.scala](../../../../test/scala/tutorial_2/DeviceInProgress.scala) { #read-protocol-1 }
@@snip [Hello.scala]($code$/scala/tutorial_2/DeviceInProgress.scala) { #read-protocol-1 }
This is a fine approach, but it limits the flexibility of the protocol. To understand why we need to talk
about message ordering and message delivery guarantees in general.
@ -139,12 +139,12 @@ can be helpful to put an additional query ID field in the message which helps us
Hence, we add one more field to our messages, so that an ID can be provided by the requester:
@@snip [Hello.scala](../../../../test/scala/tutorial_2/DeviceInProgress.scala) { #read-protocol-2 }
@@snip [Hello.scala]($code$/scala/tutorial_2/DeviceInProgress.scala) { #read-protocol-2 }
Our device actor has the responsibility to use the same ID for the response of a given query. Now we can sketch
our device actor:
@@snip [Hello.scala](../../../../test/scala/tutorial_2/DeviceInProgress.scala) { #device-with-read }
@@snip [Hello.scala]($code$/scala/tutorial_2/DeviceInProgress.scala) { #device-with-read }
We maintain the current temperature, initially set to `None`, and we simply report it back if queried. We also
added fields for the ID of the device and the group it belongs to, which we will use later.
@ -152,7 +152,7 @@ added fields for the ID of the device and the group it belongs to, which we will
We can already write a simple test for this functionality (we use ScalaTest but any other test framework can be
used with the Akka Testkit):
@@snip [Hello.scala](../../../../test/scala/tutorial_2/DeviceSpec.scala) { #device-read-test }
@@snip [Hello.scala]($code$/scala/tutorial_2/DeviceSpec.scala) { #device-read-test }
## The Write Protocol
@ -162,7 +162,7 @@ As a first attempt, we could model recording the current temperature in the devi
Such a message could possibly look like this:
@@snip [Hello.scala](../../../../test/scala/tutorial_2/DeviceInProgress.scala) { #write-protocol-1 }
@@snip [Hello.scala]($code$/scala/tutorial_2/DeviceInProgress.scala) { #write-protocol-1 }
The problem with this approach is that the sender of the record temperature message can never be sure if the message
was processed or not. We have seen that Akka does not guarantee delivery of these messages and leaves it to the
@ -172,12 +172,12 @@ Just like in the case of temperature queries and responses, it is a good idea to
Putting read and write protocol together, the device actor will look like this:
@@snip [Hello.scala](../../../../test/scala/tutorial_2/Device.scala) { #full-device }
@@snip [Hello.scala]($code$/scala/tutorial_2/Device.scala) { #full-device }
We are also responsible for writing a new test case now, exercising both the read/query and write/record functionality
together:
@@snip [Hello.scala](../../../../test/scala/tutorial_2/DeviceSpec.scala) { #device-write-read-test }
@@snip [Hello.scala]($code$/scala/tutorial_2/DeviceSpec.scala) { #device-write-read-test }
## What is Next?

View file

@ -81,7 +81,7 @@ is known up front: device groups and device actors are created on-demand. The st
Now that the steps are defined, we only need to define the messages that we will use to communicate requests and
their acknowledgement:
@@snip [Hello.scala](../../../../test/scala/tutorial_3/DeviceManager.scala) { #device-manager-msgs }
@@snip [Hello.scala]($code$/scala/tutorial_3/DeviceManager.scala) { #device-manager-msgs }
As you see, in this case, we have not included a request ID field in the messages. Since registration is usually happening
once, at the component that connects the system to some network protocol, we will usually have no use for the ID.
@ -104,7 +104,7 @@ the code looks like:
value. This is achieved by variables included in backticks, like `` `variable` ``, and it means that the pattern
only match if it contains the value of `variable` in that position.
@@snip [Hello.scala](../../../../test/scala/tutorial_3/Device.scala) { #device-with-register }
@@snip [Hello.scala]($code$/scala/tutorial_3/Device.scala) { #device-with-register }
We should not leave features untested, so we immediately write two new test cases, one exercising successful
registration, the other testing the case when IDs don't match:
@ -114,7 +114,7 @@ and fails if it receives any messages during this period. If no messages are rec
assertion passes. It is usually a good idea to keep these timeouts low (but not too low) because they add significant
test execution time otherwise.
@@snip [Hello.scala](../../../../test/scala/tutorial_3/DeviceSpec.scala) { #device-registration-tests }
@@snip [Hello.scala]($code$/scala/tutorial_3/DeviceSpec.scala) { #device-registration-tests }
## Device Group
@ -127,18 +127,18 @@ by using `forward` instead of the `!` operator. The only difference between the
sender while `!` always sets the sender to be the current actor. Just like with our device actor, we ensure that we don't
respond to wrong group IDs:
@@snip [Hello.scala](../../../../test/scala/tutorial_3/DeviceGroup.scala) { #device-group-register }
@@snip [Hello.scala]($code$/scala/tutorial_3/DeviceGroup.scala) { #device-group-register }
Just as we did with the device, we test this new functionality. We also test that the actors returned for the two
different IDs are actually different, and we also attempt to record a temperature reading for each of the devices
to see if the actors are responding.
@@snip [Hello.scala](../../../../test/scala/tutorial_3/DeviceGroupSpec.scala) { #device-group-test-registration }
@@snip [Hello.scala]($code$/scala/tutorial_3/DeviceGroupSpec.scala) { #device-group-test-registration }
It might be, that a device actor already exists for the registration request. In this case, we would like to use
the existing actor instead of a new one. We have not tested this yet, so we need to fix this:
@@snip [Hello.scala](../../../../test/scala/tutorial_3/DeviceGroupSpec.scala) { #device-group-test3 }
@@snip [Hello.scala]($code$/scala/tutorial_3/DeviceGroupSpec.scala) { #device-group-test3 }
So far, we have implemented everything for registering device actors in the group. Devices come and go, however, so
we will need a way to remove those from the `Map[String, ActorRef]`. We will assume that when a device is removed, its corresponding device actor
@ -163,13 +163,13 @@ its ID, which we need to remove it from the map of existing device to device act
need to introduce another placeholder, `Map[ActorRef, String]`, that allow us to find out the device ID corresponding to a given `ActorRef`. Putting
this together the result is:
@@snip [Hello.scala](../../../../test/scala/tutorial_3/DeviceGroup.scala) { #device-group-remove }
@@snip [Hello.scala]($code$/scala/tutorial_3/DeviceGroup.scala) { #device-group-remove }
So far we have no means to get what devices the group device actor keeps track of and, therefore, we cannot test our
new functionality yet. To make it testable, we add a new query capability (message `RequestDeviceList(requestId: Long)`) that simply lists the currently active
device IDs:
@@snip [Hello.scala](../../../../test/scala/tutorial_3/DeviceGroup.scala) { #device-group-full }
@@snip [Hello.scala]($code$/scala/tutorial_3/DeviceGroup.scala) { #device-group-full }
We almost have everything to test the removal of devices. What is missing is:
@ -183,14 +183,14 @@ We add two more test cases now. In the first, we just test that we get back the
a few devices. The second test case makes sure that the device ID is properly removed after the device actor has
been stopped:
@@snip [Hello.scala](../../../../test/scala/tutorial_3/DeviceGroupSpec.scala) { #device-group-list-terminate-test }
@@snip [Hello.scala]($code$/scala/tutorial_3/DeviceGroupSpec.scala) { #device-group-list-terminate-test }
## Device Manager
The only part that remains now is the entry point for our device manager component. This actor is very similar to
the device group actor, with the only difference that it creates device group actors instead of device actors:
@@snip [Hello.scala](../../../../test/scala/tutorial_3/DeviceManager.scala) { #device-manager-full }
@@snip [Hello.scala]($code$/scala/tutorial_3/DeviceManager.scala) { #device-manager-full }
We leave tests of the device manager as an exercise as it is very similar to the tests we have written for the group
actor.

View file

@ -41,7 +41,7 @@ that each device can be in, according to the query:
Summarizing these in message types we can add the following to `DeviceGroup`:
@@snip [Hello.scala](../../../../test/scala/tutorial_4/DeviceGroup.scala) { #query-protocol }
@@snip [Hello.scala]($code$/scala/tutorial_4/DeviceGroup.scala) { #query-protocol }
## Implementing the Query
@ -87,7 +87,7 @@ until the timeout to mark these as not available.
Putting together all these, the outline of our actor looks like this:
@@snip [Hello.scala](../../../../test/scala/tutorial_4/DeviceGroupQuery.scala) { #query-outline }
@@snip [Hello.scala]($code$/scala/tutorial_4/DeviceGroupQuery.scala) { #query-outline }
The query actor, apart from the pending timer, has one stateful aspect about it: the actors that did not answer so far or,
from the other way around, the set of actors that have replied or stopped. One way to track this state is
@ -108,7 +108,7 @@ we will discuss later. In the case of timeout, we need to simply take all the ac
(the members of the set `stillWaiting`) and put a `DeviceTimedOut` as the status in the final reply. Then we
reply to the submitter of the query with the collected results and stop the query actor:
@@snip [Hello.scala](../../../../test/scala/tutorial_4/DeviceGroupQuery.scala) { #query-state }
@@snip [Hello.scala]($code$/scala/tutorial_4/DeviceGroupQuery.scala) { #query-state }
What is not yet clear, how we will "mutate" the `answersSoFar` and `stillWaiting` data structures. One important
thing to note is that the function `waitingForReplies` **does not handle the messages directly. It returns a `Receive`
@ -137,7 +137,7 @@ only the first call will have any effect, the rest is simply ignored.
With all this knowledge, we can create the `receivedResponse` method:
@@snip [Hello.scala](../../../../test/scala/tutorial_4/DeviceGroupQuery.scala) { #query-collect-reply }
@@snip [Hello.scala]($code$/scala/tutorial_4/DeviceGroupQuery.scala) { #query-collect-reply }
It is quite natural to ask at this point, what have we gained by using the `context.become()` trick instead of
just making the `repliesSoFar` and `stillWaiting` structures mutable fields of the actor (i.e. `var`s)? In this
@ -151,7 +151,7 @@ with the solution we have used here as it helps structuring more complex actor c
Or query actor is now done:
@@snip [Hello.scala](../../../../test/scala/tutorial_4/DeviceGroupQuery.scala) { #query-full }
@@snip [Hello.scala]($code$/scala/tutorial_4/DeviceGroupQuery.scala) { #query-full }
## Testing
@ -161,27 +161,27 @@ various normal or failure scenarios. Thankfully we took the list of collaborator
to the query actor, so we can easily pass in `TestProbe` references. In our first test, we try out the case when
there are two devices and both report a temperature:
@@snip [Hello.scala](../../../../test/scala/tutorial_4/DeviceGroupQuerySpec.scala) { #query-test-normal }
@@snip [Hello.scala]($code$/scala/tutorial_4/DeviceGroupQuerySpec.scala) { #query-test-normal }
That was the happy case, but we know that sometimes devices cannot provide a temperature measurement. This
scenario is just slightly different from the previous:
@@snip [Hello.scala](../../../../test/scala/tutorial_4/DeviceGroupQuerySpec.scala) { #query-test-no-reading }
@@snip [Hello.scala]($code$/scala/tutorial_4/DeviceGroupQuerySpec.scala) { #query-test-no-reading }
We also know, that sometimes device actors stop before answering:
@@snip [Hello.scala](../../../../test/scala/tutorial_4/DeviceGroupQuerySpec.scala) { #query-test-stopped }
@@snip [Hello.scala]($code$/scala/tutorial_4/DeviceGroupQuerySpec.scala) { #query-test-stopped }
If you remember, there is another case related to device actors stopping. It is possible that we get a normal reply
from a device actor, but then receive a `Terminated` for the same actor later. In this case, we would like to keep
the first reply and not mark the device as `DeviceNotAvailable`. We should test this, too:
@@snip [Hello.scala](../../../../test/scala/tutorial_4/DeviceGroupQuerySpec.scala) { #query-test-stopped-later }
@@snip [Hello.scala]($code$/scala/tutorial_4/DeviceGroupQuerySpec.scala) { #query-test-stopped-later }
The final case is when not all devices respond in time. To keep our test relatively fast, we will construct the
`DeviceGroupQuery` actor with a smaller timeout:
@@snip [Hello.scala](../../../../test/scala/tutorial_4/DeviceGroupQuerySpec.scala) { #query-test-timeout }
@@snip [Hello.scala]($code$/scala/tutorial_4/DeviceGroupQuerySpec.scala) { #query-test-timeout }
Our query works as expected now, it is time to include this new functionality in the `DeviceGroup` actor now.
@ -190,7 +190,7 @@ Our query works as expected now, it is time to include this new functionality in
Including the query feature in the group actor is fairly simple now. We did all the heavy lifting in the query actor
itself, the group actor only needs to create it with the right initial parameters and nothing else.
@@snip [Hello.scala](../../../../test/scala/tutorial_4/DeviceGroup.scala) { #query-added }
@@snip [Hello.scala]($code$/scala/tutorial_4/DeviceGroup.scala) { #query-added }
It is probably worth to reiterate what we said at the beginning of the chapter. By keeping the temporary state
that is only relevant to the query itself in a separate actor we keep the group actor implementation very simple. It delegates
@ -202,4 +202,4 @@ would significantly improve throughput.
We close this chapter by testing that everything works together. This test is just a variant of the previous ones,
now exercising the group query feature:
@@snip [Hello.scala](../../../../test/scala/tutorial_4/DeviceGroupSpec.scala) { #group-query-integration-test }
@@snip [Hello.scala]($code$/scala/tutorial_4/DeviceGroupSpec.scala) { #group-query-integration-test }

View file

@ -204,7 +204,7 @@ Next we prepare an protocol definition using the protobuf Interface Description
the serializer code to be used on the Akka Serialization layer (notice that the schema aproach allows us to easily rename
fields, as long as the numeric identifiers of the fields do not change):
@@snip [FlightAppModels.proto]($code$/protobuf/FlightAppModels.proto) { #protobuf-read-optional-proto }
@@snip [FlightAppModels.proto]($code$/../main/protobuf/FlightAppModels.proto) { #protobuf-read-optional-proto }
The serializer implementation uses the protobuf generated classes to marshall the payloads.
Optional fields can be handled explicitly or missing values by calling the `has...` methods on the protobuf object,

Some files were not shown because too many files have changed in this diff Show more