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:
parent
413df8e0f4
commit
59f53e1a22
289 changed files with 45 additions and 45 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue