diff --git a/README.md b/README.md index 9c5370c04..461f0cd96 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,7 @@ We will start with some basic actors and have you incrementally work your way up The course is self-directed learning - you can do it at whatever pace you wish. You can [sign up here to have one Akka.NET Bootcamp lesson emailed to you daily](http://learnakka.net/ "Learn Akka.NET with Akka.NET Bootcamp") if you'd like a little help pacing yourself. -> NOTE: Currently this bootcamp only supports C# - we intend to add F# in the future. -> -> (We will also accept F# pull requests.) +> Good news! The full Akka.NET bootcamp is now available in F#! Yeah! ## What will you learn? In Akka.NET Bootcamp you will learn how to use Akka.NET actors to build reactive, concurrent applications. @@ -35,15 +33,15 @@ In Unit 1 you will learn: 5. How to create child actors and actor hierarchies, and how to supervise children with `SupervisionStrategy`. 6. How to use the Actor lifecycle to control actor startup, shutdown, and restart behavior. -**[Begin Unit 1](src/Unit-1/FSharp)**. +**[Begin Unit 1](src/Unit-1/)**. ### Unit 2 In Unit 2, we're going to get into some more of the intermediate Akka.NET features to build a more sophisticated application than what we accomplished at the end of unit 1. In Unit 2 you will learn: -1. How to use [HOCON configuration](http://getakka.net/wiki/Configuration "Akka.NET HOCON Configurations") to configure your actors via App.config and Web.config; -1. How to configure your actor's [Dispatcher](http://getakka.net/wiki/Dispatchers) to run on the Windows Forms UI thread, so actors can make operations directly on UI elements without needing to change contexts; +1. How to use [HOCON configuration](http://getakka.net/articles/concepts/configuration.html "Akka.NET HOCON Configurations") to configure your actors via App.config and Web.config; +1. How to configure your actor's [Dispatcher](http://getakka.net/articles/actors/dispatchers.html) to run on the Windows Forms UI thread, so actors can make operations directly on UI elements without needing to change contexts; 1. How to use the `Scheduler` to send recurring messages to actors; 1. How to use the [Publish-subscribe (pub-sub) pattern](http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) between actors; 1. How and why to switch actor's behavior at run-time; and @@ -94,12 +92,12 @@ src\Unit1\README.MD - table of contents and instructions for the module src\Unit1\DoThis\ - contains the .SLN and project files that you will use through all lessons -- lesson 1 src\Unit1\Lesson1\README.MD - README explaining lesson1 -src\Unit1\Lesson1\DoThis\ - C# classes, images, text files, and other junk you'll need to complete lesson1 +src\Unit1\Lesson1\DoThis\ - F# classes, images, text files, and other junk you'll need to complete lesson1 src\Unit1\Lesson1\Completed\ - Got stuck on lesson1? This folder shows the "expected" output for the lesson -- repeat for all lessons ```` -Start with the first lesson in each unit and follow the links through their README files on Github. We're going to begin with **[Unit 1, Lesson 1](src/Unit-1/FSharp/lesson1)**. +Start with the first lesson in each unit and follow the links through their README files on Github. We're going to begin with **[Unit 1, Lesson 1](src/Unit-1/lesson1)**. ### Lesson Layout Each Akka.NET Bootcamp lesson contains a README which explains the following: @@ -123,14 +121,14 @@ We will provide explanations of all key concepts throughout each lesson, but of ## Tools / prerequisites This course expects the following: -- You have some programming experience and familiarity with C# +- You have some programming experience and familiarity with F# - A Github account and basic knowledge of Git. - You are using a version of Visual Studio ([it's free now!](http://www.visualstudio.com/)) - We haven't had a chance to test these in Xamarin / on Mono yet, but that will be coming soon. If you try them there, please let us know how it goes! We are planning on having everything on all platforms ASAP. ## Enough talk, let's go! -[Let's begin!](src/Unit-1/FSharp/lesson1) +[Let's begin!](src/Unit-1/lesson1) ## About Petabridge ![Petabridge logo](images/petabridge_logo.png) diff --git a/src/Unit-1/CSharp/DoThis/App.config b/src/Unit-1/CSharp/DoThis/App.config deleted file mode 100644 index 8e1564635..000000000 --- a/src/Unit-1/CSharp/DoThis/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/DoThis/ConsoleReaderActor.cs b/src/Unit-1/CSharp/DoThis/ConsoleReaderActor.cs deleted file mode 100644 index cb3a0416d..000000000 --- a/src/Unit-1/CSharp/DoThis/ConsoleReaderActor.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor responsible for reading FROM the console. - /// Also responsible for calling . - /// - class ConsoleReaderActor : UntypedActor - { - public const string ExitCommand = "exit"; - private ActorRef _consoleWriterActor; - - public ConsoleReaderActor(ActorRef consoleWriterActor) - { - _consoleWriterActor = consoleWriterActor; - } - - protected override void OnReceive(object message) - { - var read = Console.ReadLine(); - if (!string.IsNullOrEmpty(read) && String.Equals(read, ExitCommand, StringComparison.OrdinalIgnoreCase)) - { - // shut down the system (acquire handle to system via - // this actors context) - Context.System.Shutdown(); - return; - } - - // send input to the console writer to process and print - // YOU NEED TO FILL IN HERE - - // continue reading messages from the console - // YOU NEED TO FILL IN HERE - } - - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/DoThis/ConsoleWriterActor.cs b/src/Unit-1/CSharp/DoThis/ConsoleWriterActor.cs deleted file mode 100644 index b4bc49dae..000000000 --- a/src/Unit-1/CSharp/DoThis/ConsoleWriterActor.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor responsible for serializing message writes to the console. - /// (write one message at a time, champ :) - /// - class ConsoleWriterActor : UntypedActor - { - protected override void OnReceive(object message) - { - var msg = message as string; - - // make sure we got a message - if (string.IsNullOrEmpty(msg)) - { - Console.ForegroundColor = ConsoleColor.DarkYellow; - Console.WriteLine("Please provide an input.\n"); - return; - } - - // if message has even # characters, display in red; else, green - var even = msg.Length % 2 == 0; - var color = even ? ConsoleColor.Red : ConsoleColor.Green; - var alert = even ? "Your string had an even # of characters.\n" : "Your string had an odd # of characters.\n"; - Console.ForegroundColor = color; - Console.WriteLine(alert); - Console.ResetColor(); - - } - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/DoThis/Program.cs b/src/Unit-1/CSharp/DoThis/Program.cs deleted file mode 100644 index 7f8f7be5f..000000000 --- a/src/Unit-1/CSharp/DoThis/Program.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using Akka.Actor; - -namespace WinTail -{ - #region Program - class Program - { - public static ActorSystem MyActorSystem; - - static void Main(string[] args) - { - // initialize MyActorSystem - // YOU NEED TO FILL IN HERE - - PrintInstructions(); - - // time to make your first actors! - //YOU NEED TO FILL IN HERE - // make consoleWriterActor using these props: Props.Create(() => new ConsoleWriterActor()) - // make consoleReaderActor using these props: Props.Create(() => new ConsoleReaderActor(consoleWriterActor)) - - - // tell console reader to begin - //YOU NEED TO FILL IN HERE - - // blocks the main thread from exiting until the actor system is shut down - MyActorSystem.AwaitTermination(); - } - - private static void PrintInstructions() - { - Console.WriteLine("Write whatever you want into the console!"); - Console.Write("Some lines will appear as"); - Console.ForegroundColor = ConsoleColor.DarkRed; - Console.Write(" red "); - Console.ResetColor(); - Console.Write(" and others will appear as"); - Console.ForegroundColor = ConsoleColor.Green; - Console.Write(" green! "); - Console.ResetColor(); - Console.WriteLine(); - Console.WriteLine(); - Console.WriteLine("Type 'exit' to quit this application at any time.\n"); - } - } - #endregion -} diff --git a/src/Unit-1/CSharp/DoThis/Properties/AssemblyInfo.cs b/src/Unit-1/CSharp/DoThis/Properties/AssemblyInfo.cs deleted file mode 100644 index f3dcf57d3..000000000 --- a/src/Unit-1/CSharp/DoThis/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Lesson1-1")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Lesson1-1")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("60155426-eaa6-430b-bac5-1a0c7dc0cfcb")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Unit-1/CSharp/DoThis/WinTail.csproj b/src/Unit-1/CSharp/DoThis/WinTail.csproj deleted file mode 100644 index 608becbf1..000000000 --- a/src/Unit-1/CSharp/DoThis/WinTail.csproj +++ /dev/null @@ -1,68 +0,0 @@ - - - - - Debug - AnyCPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F} - Exe - Properties - WinTail - WinTail - v4.5 - 512 - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - packages\Akka.0.8.0\lib\net45\Akka.dll - - - False - packages\Newtonsoft.Json.6.0.1\lib\net45\Newtonsoft.Json.dll - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/DoThis/WinTail.sln b/src/Unit-1/CSharp/DoThis/WinTail.sln deleted file mode 100644 index 1c0539943..000000000 --- a/src/Unit-1/CSharp/DoThis/WinTail.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinTail", "WinTail.csproj", "{E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/Unit-1/CSharp/README.md b/src/Unit-1/CSharp/README.md deleted file mode 100644 index 24a7de2e2..000000000 --- a/src/Unit-1/CSharp/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# Akka.NET Bootcamp - Unit 1: Beginning Akka.NET - -![Akka.NET logo](../../../images/akka_net_logo.png) - -In Unit 1, we will learn the fundamentals of how the actor model and Akka.NET work. - -## Concepts you'll learn - -*NIX systems have the `tail` command built-in to monitor changes to a file (such as tailing log files), whereas Windows does not. We will recreate `tail` for Windows, and use the process to learn the fundamentals. - -In Unit 1 you will learn: - -1. How to create your own `ActorSystem` and actors; -2. How to send messages actors and how to handle different types of messages; -3. How to use `Props` and `ActorRef`s to build loosely coupled systems. -4. How to use actor paths, addresses, and `ActorSelection` to send messages to actors. -5. How to create child actors and actor hierarchies, and how to supervise children with `SupervisionStrategy`. -6. How to use the Actor lifecycle to control actor startup, shutdown, and restart behavior. - -## Using Xamarin? -Since Unit 1 relies heavily on the console, you'll need to make a small tweaks before beginning. You need to set up your `WinTail` project file (not the solution) to use an **external console**. - -To set this up: - -1. Click on the `WinTail` project (not the solution) -2. Navigate to `Project > WinTail Options` in the menu -3. Inside `WinTail Options`, navigate to `Run > General` -4. Select `Run on external console` -5. Click `OK` - -Here is a demonstration of how to set it up: -![Configure Xamarin to use external console](../../images/xamarin.gif) - - -## Table of Contents - -1. **[Lesson 1 - Actors and the `ActorSystem`](lesson1/)** -2. **[Lesson 2 - Defining and Handling Messages](lesson2/)** -3. **[Lesson 3: Using `Props` and `ActorRef`s](lesson3/)** -4. **[Lesson 4: Child Actors, Hierarchies, and Supervision](lesson4/)** -5. **[Lesson 5: Looking up actors by address with `ActorSelection`](lesson5/)** -6. **[Lesson 6: The Actor Lifecycle](lesson6/)** - -## Get Started - -To get started, [go to the /DoThis/ folder](DoThis/) and open `WinTail.sln`. - -And then go to [Lesson 1](lesson1/). diff --git a/src/Unit-1/CSharp/lesson1/Completed/.nuget/NuGet.exe b/src/Unit-1/CSharp/lesson1/Completed/.nuget/NuGet.exe deleted file mode 100644 index 8dd7e45ae..000000000 Binary files a/src/Unit-1/CSharp/lesson1/Completed/.nuget/NuGet.exe and /dev/null differ diff --git a/src/Unit-1/CSharp/lesson1/Completed/App.config b/src/Unit-1/CSharp/lesson1/Completed/App.config deleted file mode 100644 index 8e1564635..000000000 --- a/src/Unit-1/CSharp/lesson1/Completed/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson1/Completed/ConsoleReaderActor.cs b/src/Unit-1/CSharp/lesson1/Completed/ConsoleReaderActor.cs deleted file mode 100644 index e6239dfdc..000000000 --- a/src/Unit-1/CSharp/lesson1/Completed/ConsoleReaderActor.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor responsible for reading FROM the console. - /// Also responsible for calling . - /// - class ConsoleReaderActor : UntypedActor - { - public const string ExitCommand = "exit"; - private ActorRef _consoleWriterActor; - - public ConsoleReaderActor(ActorRef consoleWriterActor) - { - _consoleWriterActor = consoleWriterActor; - } - - protected override void OnReceive(object message) - { - var read = Console.ReadLine(); - if (!string.IsNullOrEmpty(read) && String.Equals(read, ExitCommand, StringComparison.OrdinalIgnoreCase)) - { - // shut down the system (acquire handle to system via - // this actors context) - Context.System.Shutdown(); - return; - } - - // send input to the console writer to process and print - _consoleWriterActor.Tell(read); - - // continue reading messages from the console - Self.Tell("continue"); - } - - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson1/Completed/ConsoleWriterActor.cs b/src/Unit-1/CSharp/lesson1/Completed/ConsoleWriterActor.cs deleted file mode 100644 index b4bc49dae..000000000 --- a/src/Unit-1/CSharp/lesson1/Completed/ConsoleWriterActor.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor responsible for serializing message writes to the console. - /// (write one message at a time, champ :) - /// - class ConsoleWriterActor : UntypedActor - { - protected override void OnReceive(object message) - { - var msg = message as string; - - // make sure we got a message - if (string.IsNullOrEmpty(msg)) - { - Console.ForegroundColor = ConsoleColor.DarkYellow; - Console.WriteLine("Please provide an input.\n"); - return; - } - - // if message has even # characters, display in red; else, green - var even = msg.Length % 2 == 0; - var color = even ? ConsoleColor.Red : ConsoleColor.Green; - var alert = even ? "Your string had an even # of characters.\n" : "Your string had an odd # of characters.\n"; - Console.ForegroundColor = color; - Console.WriteLine(alert); - Console.ResetColor(); - - } - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson1/Completed/Program.cs b/src/Unit-1/CSharp/lesson1/Completed/Program.cs deleted file mode 100644 index d1bf0fb17..000000000 --- a/src/Unit-1/CSharp/lesson1/Completed/Program.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using Akka.Actor; - -namespace WinTail -{ - class Program - { - public static ActorSystem MyActorSystem; - - - static void Main(string[] args) - { - // make an actor system - MyActorSystem = ActorSystem.Create("MyActorSystem"); - - PrintInstructions(); - - // make our first actors! - ActorRef consoleWriterActor = MyActorSystem.ActorOf(Props.Create(() => new ConsoleWriterActor()), - "consoleWriterActor"); - ActorRef consoleReaderActor = - MyActorSystem.ActorOf(Props.Create(() => new ConsoleReaderActor(consoleWriterActor)), - "consoleReaderActor"); - - // tell console reader to begin - consoleReaderActor.Tell("start"); - - // blocks the main thread from exiting until the actor system is shut down - MyActorSystem.AwaitTermination(); - } - - private static void PrintInstructions() - { - Console.WriteLine("Write whatever you want into the console!"); - Console.Write("Some lines will appear as"); - Console.ForegroundColor = ConsoleColor.DarkRed; - Console.Write(" red "); - Console.ResetColor(); - Console.Write(" and others will appear as"); - Console.ForegroundColor = ConsoleColor.Green; - Console.Write(" green! "); - Console.ResetColor(); - Console.WriteLine(); - Console.WriteLine(); - Console.WriteLine("Type 'exit' to quit this application at any time.\n"); - } - } -} diff --git a/src/Unit-1/CSharp/lesson1/Completed/Properties/AssemblyInfo.cs b/src/Unit-1/CSharp/lesson1/Completed/Properties/AssemblyInfo.cs deleted file mode 100644 index f3dcf57d3..000000000 --- a/src/Unit-1/CSharp/lesson1/Completed/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Lesson1-1")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Lesson1-1")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("60155426-eaa6-430b-bac5-1a0c7dc0cfcb")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Unit-1/CSharp/lesson1/Completed/WinTail.csproj b/src/Unit-1/CSharp/lesson1/Completed/WinTail.csproj deleted file mode 100644 index 19c0ffc5e..000000000 --- a/src/Unit-1/CSharp/lesson1/Completed/WinTail.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - - Debug - AnyCPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F} - Exe - Properties - WinTail - WinTail - v4.5 - 512 - .\ - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - packages\Akka.0.8.0\lib\net45\Akka.dll - - - False - packages\Newtonsoft.Json.6.0.1\lib\net45\Newtonsoft.Json.dll - - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson1/Completed/WinTail.sln b/src/Unit-1/CSharp/lesson1/Completed/WinTail.sln deleted file mode 100644 index 8fe8391dc..000000000 --- a/src/Unit-1/CSharp/lesson1/Completed/WinTail.sln +++ /dev/null @@ -1,29 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinTail", "WinTail.csproj", "{E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{FE80AC22-4BB2-4455-AEE5-CBDD62EABC4E}" - ProjectSection(SolutionItems) = preProject - .nuget\NuGet.Config = .nuget\NuGet.Config - .nuget\NuGet.exe = .nuget\NuGet.exe - .nuget\NuGet.targets = .nuget\NuGet.targets - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/Unit-1/CSharp/lesson1/Completed/packages.config b/src/Unit-1/CSharp/lesson1/Completed/packages.config deleted file mode 100644 index 88dee0f6e..000000000 --- a/src/Unit-1/CSharp/lesson1/Completed/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson1/README.md b/src/Unit-1/CSharp/lesson1/README.md deleted file mode 100644 index 13b265166..000000000 --- a/src/Unit-1/CSharp/lesson1/README.md +++ /dev/null @@ -1,168 +0,0 @@ -# Lesson 1.1: Actors and the `ActorSystem` -Here we go! Welcome to lesson 1. - -In this lesson, you will make your first actors and be introduced to the fundamentals of [Akka.NET](http://getakka.net/). - -## Key concepts / background -In this first lesson, you will learn the basics by creating a console app with your first actor system and simple actors within it. - -We will be creating two actors, one to read from the console, and one to write to it after doing some basic processing. - -### What is an actor? -An "actor" is really just an analog for a human participant in a system. It's an entity, an object, that can do things and communicate. - -> We're going to assume that you're familiar with object-oriented programming (OOP). The actor model is very similar to object-oriented programming (OOP) - just like how everything is an object in OOP, in the actor model ***everything is an actor***. - -Repeat this train of thought to yourself: everything is an actor. Everything is an actor. Everything is an actor! Think of designing your system like a hierarchy of people, with tasks being split up and delegated until they become small enough to be handled concisely by one actor. - -For now, we suggest you think of it like this: in OOP, you try to give every object a single, well-defined purpose, right? Well, the actor model is no different, except now the objects that you give a clear purpose to just happen to be actors. - -**Further reading: [What is an Akka.NET Actor](http://petabridge.com/blog/akkadotnet-what-is-an-actor/)?** - -### How do actors communicate? -Actors communicate with each other just as humans do, by exchanging messages. These messages are just plain old C# classes. - -```csharp -//this is a message! -public class SomeMessage{ - public int SomeValue {get; set} -} -``` - -We go into messages in detail in the next lesson, so don't worry about it for now. All you need to know is that you send messages by `Tell()`ing them to another actor. - -```csharp -//send a string to an actor -someActorRef.Tell("this is a message too!"); -``` - -### What can an actor do? -Anything you can code. Really :) - -You code actors to handle messages they receive, and actors can do whatever you need them to in order to handle a message. Talk to a database, write to a file, change an internal variable, or anything else you might need. - -But in addition to processing messages it receives, and actors are also able to do 3 other special actions: - -1. Create other actors; -1. Send messages to other actors (such as the `Sender` of the current message;) or -1. Change its own behavior and process the next message it receives differently. - -Actors are inherently asynchronous (more on this in a future lesson), and there is nothing about the [Actor Model](https://en.wikipedia.org/wiki/Actor_model) that says which of the above an actor must do, or the order it has to do them in. It's up to you. - -### What kinds of actors are there? -All types of actors inherit from `UntypedActor`, but don't worry about that now. We'll cover different actor types later. - -In Unit 1 all of your actors will inherit from [`UntypedActor`](http://getakka.net/wiki/Working%20with%20actors#untypedactor-api "Akka.NET - UntypedActor API"). - -### How do you make an actor? -There are 2 key things to know about creating an actor: - -1. All actors are created within a certain context. That is, they are "actor of" a context. -1. Actors need `Props` to be created. A `Props` object is just an object that encapsulates the formula for making a given kind of actor. - -We'll be going into `Props` in depth in lesson 3, so for now don't worry about it much. We've provided the `Props` for you in the code, so you just have to figure out how to use `Props` to make an actor. - -The hint we'll give you is that your first actors will be created within the context of your actor system itself. See the exercise instructions for more. - -### What is an `ActorSystem`? -An `ActorSystem` is a reference to the underlying system and Akka.NET framework. All actors live within the context of this actor system. You'll need to create your first actors from the context of this `ActorSystem`. - -By the way, the `ActorSystem` is a heavy object: create only one per application. - -Aaaaaaand... go! That's enough conceptual stuff for now, so dive right in and make your first actors. - -## Exercise -Let's dive in! - -> Note: Within the sample code there are sections clearly marked `"YOU NEED TO FILL IN HERE"` - find those regions of code and begin filling them in with the appropriate functionality in order to complete your goals. - -### Launch the fill-in-the-blank sample -Go to the [DoThis](../DoThis/) folder and open [WinTail](../DoThis/WinTail.sln) in Visual Studio. The solution consists of a simple console application and only one Visual Studio project file. - -You will use this solution file through all of Unit 1. - -### Install the latest Akka.NET NuGet package -In the Package Manager Console, type the following command: - -``` -Install-Package Akka -``` - -This will install the latest Akka.NET binaries, which you will need in order to compile this sample. - -Then you'll need to add the `using` namespace to the top of `Program.cs`: - - -```csharp -// in Program.cs -using Akka.Actor; -``` - -### Make your first `ActorSystem` -Go to `Program.cs` and add this to create your first actor system: - -```csharp -MyActorSystem = ActorSystem.Create("MyActorSystem"); -``` -> -> **NOTE:** When creating `Props`, `ActorSystem`, or `ActorRef` you will very rarely see the `new` keyword. These objects must be created through the factory methods built into Akka.NET. If you're using `new` you might be making a mistake. - -### Make ConsoleReaderActor & ConsoleWriterActor -The actor classes themselves are already defined, but you will have to make your first actors. - -Again, in `Program.cs`, add this just below where you made your `ActorSystem`: - -```csharp -var consoleWriterActor = MyActorSystem.ActorOf(Props.Create(() => new ConsoleWriterActor())); -var consoleReaderActor = MyActorSystem.ActorOf(Props.Create(() => new ConsoleReaderActor(consoleWriterActor))); -``` - -We will get into the details of `Props` and `ActorRef`s in lesson 3, so don't worry about them much for now. Just know that this is how you make an actor. - -### Have ConsoleReaderActor Send a Message to ConsoleWriterActor -Time to put your first actors to work! - -You will need to do the following: - -1. Have ConsoleReaderActor send a message to ConsoleWriterActor containing the content that it just read from the console. - - ```csharp - // in ConsoleReaderActor.cs - _consoleWriterActor.Tell(read); - ``` - -2. Have ConsoleReaderActor send a message to itself after sending a message to ConsoleWriterActor. This is what keeps the read loop going. - - ```csharp - // in ConsoleReaderActor.cs - Self.Tell("continue"); - ``` -3. Send an initial message to ConsoleReaderActor in order to get it to start reading from the console. - - ```csharp - // in Program.cs - consoleReaderActor.Tell("start"); - ``` - -### Step 5: Build and Run! -Once you've made your edits, press `F5` to compile and run the sample in Visual Studio. - -You should see something like this, when it is working correctly: -![Petabridge Akka.NET Bootcamp Lesson 1.1 Correct Output](Images/example.png) - - -### Once you're done -Compare your code to the code in the [Completed](Completed/) folder to see what the instructors included in their samples. - -## Great job! Onto Lesson 2! -Awesome work! Well done on completing your first lesson. - -**Let's move onto [Lesson 2 - Defining and Handling Different Types of Messages](../lesson2).** - -## Any questions? -**Don't be afraid to ask questions** :). - -Come ask any questions you have, big or small, [in this ongoing Bootcamp chat with the Petabridge & Akka.NET teams](https://gitter.im/petabridge/akka-bootcamp). - -### Problems with the code? -If there is a problem with the code running, or something else that needs to be fixed in this lesson, please [create an issue](/issues) and we'll get right on it. This will benefit everyone going through Bootcamp. diff --git a/src/Unit-1/CSharp/lesson2/Completed/App.config b/src/Unit-1/CSharp/lesson2/Completed/App.config deleted file mode 100644 index 8e1564635..000000000 --- a/src/Unit-1/CSharp/lesson2/Completed/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson2/Completed/ConsoleReaderActor.cs b/src/Unit-1/CSharp/lesson2/Completed/ConsoleReaderActor.cs deleted file mode 100644 index 9556a9011..000000000 --- a/src/Unit-1/CSharp/lesson2/Completed/ConsoleReaderActor.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor responsible for reading FROM the console. - /// Also responsible for calling . - /// - class ConsoleReaderActor : UntypedActor - { - public const string StartCommand = "start"; - public const string ExitCommand = "exit"; - private readonly ActorRef _consoleWriterActor; - - public ConsoleReaderActor(ActorRef consoleWriterActor) - { - _consoleWriterActor = consoleWriterActor; - } - - protected override void OnReceive(object message) - { - if (message.Equals(StartCommand)) - { - DoPrintInstructions(); - } - else if (message is Messages.InputError) - { - _consoleWriterActor.Tell(message as Messages.InputError); - } - - GetAndValidateInput(); - } - - - #region Internal methods - private void DoPrintInstructions() - { - Console.WriteLine("Write whatever you want into the console!"); - Console.WriteLine("Some entries will pass validation, and some won't...\n\n"); - Console.WriteLine("Type 'exit' to quit this application at any time.\n"); - } - - - /// - /// Reads input from console, validates it, then signals appropriate response - /// (continue processing, error, success, etc.). - /// - private void GetAndValidateInput() - { - var message = Console.ReadLine(); - if (string.IsNullOrEmpty(message)) - { - // signal that the user needs to supply an input, as previously - // received input was blank - Self.Tell(new Messages.NullInputError("No input received.")); - } - else if (String.Equals(message, ExitCommand, StringComparison.OrdinalIgnoreCase)) - { - // shut down the entire actor system (allows the process to exit) - Context.System.Shutdown(); - } - else - { - var valid = IsValid(message); - if (valid) - { - _consoleWriterActor.Tell(new Messages.InputSuccess("Thank you! Message was valid.")); - - // continue reading messages from console - Self.Tell(new Messages.ContinueProcessing()); - } - else - { - Self.Tell(new Messages.ValidationError("Invalid: input had odd number of characters.")); - } - } - } - - /// - /// Validates . - /// Currently says messages are valid if contain even number of characters. - /// - /// - /// - private static bool IsValid(string message) - { - var valid = message.Length % 2 == 0; - return valid; - } - - #endregion - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson2/Completed/ConsoleWriterActor.cs b/src/Unit-1/CSharp/lesson2/Completed/ConsoleWriterActor.cs deleted file mode 100644 index d69212883..000000000 --- a/src/Unit-1/CSharp/lesson2/Completed/ConsoleWriterActor.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Net.Configuration; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor responsible for serializing message writes to the console. - /// (write one message at a time, champ :) - /// - class ConsoleWriterActor : UntypedActor - { - protected override void OnReceive(object message) - { - if (message is Messages.InputError) - { - var msg = message as Messages.InputError; - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine(msg.Reason); - } - else if (message is Messages.InputSuccess) - { - var msg = message as Messages.InputSuccess; - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine(msg.Reason); - } - else - { - Console.WriteLine(message); - } - - Console.ResetColor(); - } - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson2/Completed/Messages.cs b/src/Unit-1/CSharp/lesson2/Completed/Messages.cs deleted file mode 100644 index ad61dd9dd..000000000 --- a/src/Unit-1/CSharp/lesson2/Completed/Messages.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WinTail -{ - class Messages - { - - #region Neutral/system messages - /// - /// Marker class to continue processing. - /// - public class ContinueProcessing { } - #endregion - - #region Success messages - /// - /// Base class for signalling that user input was valid. - /// - public class InputSuccess - { - public InputSuccess(string reason) - { - Reason = reason; - } - - public string Reason { get; private set; } - } - #endregion - - #region Error messages - /// - /// Base class for signalling that user input was invalid. - /// - public class InputError - { - public InputError(string reason) - { - Reason = reason; - } - - public string Reason { get; private set; } - } - - /// - /// User provided blank input. - /// - public class NullInputError : InputError - { - public NullInputError(string reason) : base(reason) { } - } - - /// - /// User provided invalid input (currently, input w/ odd # chars) - /// - public class ValidationError : InputError - { - public ValidationError(string reason) : base(reason) { } - } - #endregion - } -} diff --git a/src/Unit-1/CSharp/lesson2/Completed/Program.cs b/src/Unit-1/CSharp/lesson2/Completed/Program.cs deleted file mode 100644 index 8840c824f..000000000 --- a/src/Unit-1/CSharp/lesson2/Completed/Program.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using Akka.Actor; - -namespace WinTail -{ - #region Program - class Program - { - public static ActorSystem MyActorSystem; - - static void Main(string[] args) - { - // make an actor system - MyActorSystem = ActorSystem.Create("MyActorSystem"); - - // make our first actors! - ActorRef consoleWriterActor = MyActorSystem.ActorOf(Props.Create(() => new ConsoleWriterActor()), - "consoleWriterActor"); - ActorRef consoleReaderActor = - MyActorSystem.ActorOf(Props.Create(() => new ConsoleReaderActor(consoleWriterActor)), - "consoleReaderActor"); - - // tell console reader to begin - consoleReaderActor.Tell(ConsoleReaderActor.StartCommand); - - // blocks the main thread from exiting until the actor system is shut down - MyActorSystem.AwaitTermination(); - } - - } - #endregion -} diff --git a/src/Unit-1/CSharp/lesson2/Completed/Properties/AssemblyInfo.cs b/src/Unit-1/CSharp/lesson2/Completed/Properties/AssemblyInfo.cs deleted file mode 100644 index f3dcf57d3..000000000 --- a/src/Unit-1/CSharp/lesson2/Completed/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Lesson1-1")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Lesson1-1")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("60155426-eaa6-430b-bac5-1a0c7dc0cfcb")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Unit-1/CSharp/lesson2/Completed/WinTail.csproj b/src/Unit-1/CSharp/lesson2/Completed/WinTail.csproj deleted file mode 100644 index 8543b02bf..000000000 --- a/src/Unit-1/CSharp/lesson2/Completed/WinTail.csproj +++ /dev/null @@ -1,69 +0,0 @@ - - - - - Debug - AnyCPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F} - Exe - Properties - WinTail - Lesson1-1 - v4.5 - 512 - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - packages\Akka.0.8.0\lib\net45\Akka.dll - - - False - packages\Newtonsoft.Json.6.0.1\lib\net45\Newtonsoft.Json.dll - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson2/Completed/WinTail.sln b/src/Unit-1/CSharp/lesson2/Completed/WinTail.sln deleted file mode 100644 index 1c0539943..000000000 --- a/src/Unit-1/CSharp/lesson2/Completed/WinTail.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinTail", "WinTail.csproj", "{E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/Unit-1/CSharp/lesson2/Completed/packages.config b/src/Unit-1/CSharp/lesson2/Completed/packages.config deleted file mode 100644 index 88dee0f6e..000000000 --- a/src/Unit-1/CSharp/lesson2/Completed/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson2/Images/working_lesson2.jpg b/src/Unit-1/CSharp/lesson2/Images/working_lesson2.jpg deleted file mode 100644 index 22ec84c8f..000000000 Binary files a/src/Unit-1/CSharp/lesson2/Images/working_lesson2.jpg and /dev/null differ diff --git a/src/Unit-1/CSharp/lesson2/README.md b/src/Unit-1/CSharp/lesson2/README.md deleted file mode 100644 index b7eca1cc6..000000000 --- a/src/Unit-1/CSharp/lesson2/README.md +++ /dev/null @@ -1,337 +0,0 @@ -# Lesson 1.2: Defining and Handling Messages -In this lesson, you will make your own message types and use learn how to control processing flow within your actors based on your custom messages. Doing so will teach you the fundamentals of communicating in a message- and event-driven manner within your actor system. - -This lesson picks up right where Lesson 1 left off, and continues extending our budding systems of console actors. In addition to defining our own messages, we'll also add some simple validation for the input we enter and take action based on the results of that validation. - -## Key concepts / background -### What is a message? -Any POCO can be a message. A message can be a `string`, a value like `int`, a type, an object that implements an interface... whatever you want. - -That being said, the recommended approach is to make your own custom messages into semantically named classes, and to encapsulate any state you want inside those classes (e.g. store a `Reason` inside a `ValidationFailed` class... hint, hint...). - -### How do I send an actor a message? -As you saw in the first lesson, you `Tell()` the actor the message. - -### How do I handle a message? -This is entirely up to you, and doesn't really have much to do with Akka.NET. You can handle (or not handle) a message as you choose within an actor. - -### What happens if my actor receives a message it doesn't know how to handle? -Actors ignore messages they don't know how to handle. Whether or not this ignored message is logged as such depends on the type of actor. - -With an `UntypedActor`, unhandled messages are not logged as unhandled unless you manually mark them as such, like so: - -```csharp - -class MyActor : UntypedActor -{ - protected override void OnReceive(object message) - { - if (message is Messages.InputError) - { - var msg = message as Messages.InputError; - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine(msg.Reason); - } - else - { - Unhandled(message); - } - } -} -``` - -However, in a `ReceiveActor`—which we cover in Unit 2—unhandled messages are automatically sent to `Unhandled` so the logging is done for you. - -### How do my actors respond to messages? -This is up to you - you can respond by simply processing the message, replying to the `Sender`, forwarding the message onto another actor, or doing nothing at all. - -> **NOTE:** Whenever your actor receives a message, it will always have the sender of the current message available via the `Sender` property inside your actor. - -## Exercise -In this exercise, we will introduce some basic validation into our system. We will then use custom message types to signal the results of that validation back to the user. - -### Phase 1: Define your own message types -#### Add a new class called `Messages` and the corresponding file, `Messages.cs`. -This is the class we'll use to define system-level messages that we can use to signal events. The pattern we'll be using is to turn events into messages. That is, when an event occurs, we will send an appropriate message class to the actor(s) that need to know about it, and then listen for / respond to that message as needed in the receiving actors. - -#### Add regions for each message type -Add three regions for different types of messages to the file. Next we'll be creating our own message classes that we'll use to signify events. - -```csharp -// in Messages.cs -#region Neutral/system messages -#endregion - -#region Success messages -#endregion - -#region Error messages -#endregion -``` - -In these regions we will define custom message types to signal these situations: - - user provided blank input - - user provided invalid input - - user provided valid input - - -#### Make `ContinueProcessing` message -Define a marker message class in the `Neutral/system messages` region that we'll use to signal to continue processing (the "blank input" case): - -```csharp -// in Messages.cs -#region Neutral/system messages -/// -/// Marker class to continue processing. -/// -public class ContinueProcessing { } -#endregion -``` - -#### Make `InputSuccess` message -Define an `InputSuccess` class in the `Success messages` region. We'll use this to signal that the user's input was good and passed validation (the "valid input" case): - -```csharp -#region Success messages -// in Messages.cs -/// -/// Base class for signalling that user input was valid. -/// -public class InputSuccess -{ - public InputSuccess(string reason) - { - Reason = reason; - } - - public string Reason { get; private set; } -} -#endregion -``` - -#### Make `InputError` messages -Define the following `InputError` classes in the `Error messages` region. We'll use these messages to signal invalid input occurring (the "invalid input" cases): - -```csharp -// in Messages.cs -#region Error messages -/// -/// Base class for signalling that user input was invalid. -/// -public class InputError -{ - public InputError(string reason) - { - Reason = reason; - } - - public string Reason { get; private set; } -} - -/// -/// User provided blank input. -/// -public class NullInputError : InputError -{ - public NullInputError(string reason) : base(reason) { } -} - -/// -/// User provided invalid input (currently, input w/ odd # chars) -/// -public class ValidationError : InputError -{ - public ValidationError(string reason) : base(reason) { } -} -#endregion -``` - - -> **NOTE:** You can compare your final `Messages.cs` to [Messages.cs](Completed/Messages.cs/) to make sure you're set up right before we go on. - -### Phase 2: Turn events into messages and send them -Great! Now that we've got messages classes set up to wrap our events, let's use them in `ConsoleReaderActor` and `ConsoleWriterActor`. - -#### Update `ConsoleReaderActor` -Add the following internal message type to `ConsoleReaderActor`: -```csharp -// in ConsoleReaderActor -public const string StartCommand = "start"; -``` - -Update the `Main` method to use `ConsoleReaderActor.StartCommand`: - -Replace this: - -```csharp -// in Program.cs -// tell console reader to begin -consoleReaderActor.Tell("start"); -``` - -with this: - -```csharp -// in Program.cs -// tell console reader to begin -consoleReaderActor.Tell(ConsoleReaderActor.StartCommand); -``` - -Replace the `OnReceive` method of `ConsoleReaderActor` as follows. Notice that we're now listening for our custom `InputError` messages, and taking action when we get an error. - -```csharp -// in ConsoleReaderActor -protected override void OnReceive(object message) -{ - if (message.Equals(StartCommand)) - { - DoPrintInstructions(); - } - else if (message is Messages.InputError) - { - _consoleWriterActor.Tell(message as Messages.InputError); - } - - GetAndValidateInput(); -} -``` - -While we're at it, let's add `DoPrintInstructions()`, `GetAndValidateInput()`, `IsValid()` to `ConsoleReaderActor`. These are internal methods that our `ConsoleReaderActor` will use to get input from the console and determine if it is valid. (Currently, "valid" just means that the input had an even number of characters. It's an arbitrary placeholder.) - -```csharp -// in ConsoleReaderActor, after OnReceive() -#region Internal methods -private void DoPrintInstructions() -{ - Console.WriteLine("Write whatever you want into the console!"); - Console.WriteLine("Some entries will pass validation, and some won't...\n\n"); - Console.WriteLine("Type 'exit' to quit this application at any time.\n"); -} - -/// -/// Reads input from console, validates it, then signals appropriate response -/// (continue processing, error, success, etc.). -/// -private void GetAndValidateInput() -{ - var message = Console.ReadLine(); - if (string.IsNullOrEmpty(message)) - { - // signal that the user needs to supply an input, as previously - // received input was blank - Self.Tell(new Messages.NullInputError("No input received.")); - } - else if (String.Equals(message, ExitCommand, StringComparison.OrdinalIgnoreCase)) - { - // shut down the entire actor system (allows the process to exit) - Context.System.Shutdown(); - } - else - { - var valid = IsValid(message); - if (valid) - { - _consoleWriterActor.Tell(new Messages.InputSuccess("Thank you! Message was valid.")); - - // continue reading messages from console - Self.Tell(new Messages.ContinueProcessing()); - } - else - { - Self.Tell(new Messages.ValidationError("Invalid: input had odd number of characters.")); - } - } -} - -/// -/// Validates . -/// Currently says messages are valid if contain even number of characters. -/// -/// -/// -private static bool IsValid(string message) -{ - var valid = message.Length % 2 == 0; - return valid; -} -#endregion -``` - -#### Update `Program` -First, remove the definition and call to `PrintInstructions()` from `Program.cs`. - -Now that `ConsoleReaderActor` has its own well-defined `StartCommand`, let's go ahead and use that instead of hardcoding the string "start" into the message. - -As a quick checkpoint, your `Main()` should now look like this: -```csharp -static void Main(string[] args) -{ - // initialize MyActorSystem - MyActorSystem = ActorSystem.Create("MyActorSystem"); - - var consoleWriterActor = MyActorSystem.ActorOf(Props.Create(() => new ConsoleWriterActor())); - var consoleReaderActor = MyActorSystem.ActorOf(Props.Create(() => new ConsoleReaderActor(consoleWriterActor))); - - // tell console reader to begin - consoleReaderActor.Tell(ConsoleReaderActor.StartCommand); - - // blocks the main thread from exiting until the actor system is shut down - MyActorSystem.AwaitTermination(); -} -``` - -Not much has changed here, just a bit of cleanup. - -#### Update `ConsoleWriterActor` -Now, let's get `ConsoleWriterActor` to handle these new types of messages. - -Change the `OnReceive` method of `ConsoleWriterActor` as follows: - -```csharp -// in ConsoleWriterActor.cs -protected override void OnReceive(object message) -{ - if (message is Messages.InputError) - { - var msg = message as Messages.InputError; - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine(msg.Reason); - } - else if (message is Messages.InputSuccess) - { - var msg = message as Messages.InputSuccess; - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine(msg.Reason); - } - else - { - Console.WriteLine(message); - } - - Console.ResetColor(); -} -``` - -As you can see here, we are making `ConsoleWriterActor` pattern match against the type of message it receives, and take different actions according to what type of message it receives. - -### Phase 3: Build and run! -You should now have everything you need in place to be able to build and run. Give it a try! - -If everything is working as it should, you should see an output like this: -![Petabridge Akka.NET Bootcamp Lesson 1.2 Correct Output](Images/working_lesson2.jpg) - -### Once you're done -Compare your code to the solution in the [Completed](Completed/) folder to see what the instructors included in their samples. - -## Great job! Onto Lesson 3! -Awesome work! Well done on completing this lesson. - -**Let's move onto [Lesson 3 - `Props` and `ActorRef`s](../lesson3).** - -## Any questions? -**Don't be afraid to ask questions** :). - -Come ask any questions you have, big or small, [in this ongoing Bootcamp chat with the Petabridge & Akka.NET teams](https://gitter.im/petabridge/akka-bootcamp). - -### Problems with the code? -If there is a problem with the code running, or something else that needs to be fixed in this lesson, please [create an issue](/issues) and we'll get right on it. This will benefit everyone going through Bootcamp. diff --git a/src/Unit-1/CSharp/lesson3/Completed/App.config b/src/Unit-1/CSharp/lesson3/Completed/App.config deleted file mode 100644 index 8e1564635..000000000 --- a/src/Unit-1/CSharp/lesson3/Completed/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson3/Completed/ConsoleReaderActor.cs b/src/Unit-1/CSharp/lesson3/Completed/ConsoleReaderActor.cs deleted file mode 100644 index 1a0e7c486..000000000 --- a/src/Unit-1/CSharp/lesson3/Completed/ConsoleReaderActor.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor responsible for reading FROM the console. - /// Also responsible for calling . - /// - class ConsoleReaderActor : UntypedActor - { - public const string StartCommand = "start"; - public const string ExitCommand = "exit"; - private readonly ActorRef _validationActor; - - public ConsoleReaderActor(ActorRef validationActor) - { - _validationActor = validationActor; - } - - protected override void OnReceive(object message) - { - if (message.Equals(StartCommand)) - { - DoPrintInstructions(); - } - - GetAndValidateInput(); - } - - - #region Internal methods - private void DoPrintInstructions() - { - Console.WriteLine("Write whatever you want into the console!"); - Console.WriteLine("Some entries will pass validation, and some won't...\n\n"); - Console.WriteLine("Type 'exit' to quit this application at any time.\n"); - } - - - /// - /// Reads input from console, validates it, then signals appropriate response - /// (continue processing, error, success, etc.). - /// - private void GetAndValidateInput() - { - var message = Console.ReadLine(); - if (!string.IsNullOrEmpty(message) && String.Equals(message, ExitCommand, StringComparison.OrdinalIgnoreCase)) - { - // if user typed ExitCommand, shut down the entire actor system (allows the process to exit) - Context.System.Shutdown(); - return; - } - - // otherwise, just hand message off to validation actor (by telling its actor ref) - _validationActor.Tell(message); - } - #endregion - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson3/Completed/ConsoleWriterActor.cs b/src/Unit-1/CSharp/lesson3/Completed/ConsoleWriterActor.cs deleted file mode 100644 index 05efe1a08..000000000 --- a/src/Unit-1/CSharp/lesson3/Completed/ConsoleWriterActor.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor responsible for serializing message writes to the console. - /// (write one message at a time, champ :) - /// - class ConsoleWriterActor : UntypedActor - { - protected override void OnReceive(object message) - { - if (message is Messages.InputError) - { - var msg = message as Messages.InputError; - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine(msg.Reason); - } - else if (message is Messages.InputSuccess) - { - var msg = message as Messages.InputSuccess; - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine(msg.Reason); - } - else - { - Console.WriteLine(message); - } - - Console.ResetColor(); - } - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson3/Completed/Messages.cs b/src/Unit-1/CSharp/lesson3/Completed/Messages.cs deleted file mode 100644 index f322a76ed..000000000 --- a/src/Unit-1/CSharp/lesson3/Completed/Messages.cs +++ /dev/null @@ -1,59 +0,0 @@ -namespace WinTail -{ - class Messages - { - - #region Neutral/system messages - /// - /// Marker class to continue processing. - /// - public class ContinueProcessing { } - #endregion - - #region Success messages - /// - /// Base class for signalling that user input was valid. - /// - public class InputSuccess - { - public InputSuccess(string reason) - { - Reason = reason; - } - - public string Reason { get; private set; } - } - #endregion - - #region Error messages - /// - /// Base class for signalling that user input was invalid. - /// - public class InputError - { - public InputError(string reason) - { - Reason = reason; - } - - public string Reason { get; private set; } - } - - /// - /// User provided blank input. - /// - public class NullInputError : InputError - { - public NullInputError(string reason) : base(reason) { } - } - - /// - /// User provided invalid input (currently, input w/ odd # chars) - /// - public class ValidationError : InputError - { - public ValidationError(string reason) : base(reason) { } - } - #endregion - } -} diff --git a/src/Unit-1/CSharp/lesson3/Completed/Program.cs b/src/Unit-1/CSharp/lesson3/Completed/Program.cs deleted file mode 100644 index 2d310191b..000000000 --- a/src/Unit-1/CSharp/lesson3/Completed/Program.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Akka.Actor; - -namespace WinTail -{ - class Program - { - public static ActorSystem MyActorSystem; - - static void Main(string[] args) - { - // make an actor system - MyActorSystem = ActorSystem.Create("MyActorSystem"); - - // this is here to show you what NOT to do - // this approach to props has no type safety - // it will compile, but can easily blow up in your face at runtime :( - // UNCOMMENT THE BELOW TWO LINES, BUILD THE SOLUTION, AND THEN TRY TO RUN IT TO SEE - //Props fakeActorProps = Props.Create(typeof(FakeActor)); - //ActorRef fakeActor = MyActorSystem.ActorOf(fakeActorProps, "fakeActor"); - - // set up actors, using props (split props onto own line so easier to read) - Props consoleWriterProps = Props.Create(); - ActorRef consoleWriterActor = MyActorSystem.ActorOf(consoleWriterProps, "consoleWriterActor"); - - Props validationActorProps = Props.Create(() => new ValidationActor(consoleWriterActor)); - ActorRef validationActor = MyActorSystem.ActorOf(validationActorProps, "validationActor"); - - Props consoleReaderProps = Props.Create(validationActor); - ActorRef consoleReaderActor = MyActorSystem.ActorOf(consoleReaderProps, "consoleReaderActor"); - - // tell console reader to begin - consoleReaderActor.Tell(ConsoleReaderActor.StartCommand); - - // blocks the main thread from exiting until the actor system is shut down - MyActorSystem.AwaitTermination(); - } - - /// - /// Fake actor / marker class. Does nothing at all, and not even an actor actually. - /// Here to show why you shouldn't use typeof approach to Props. - /// - public class FakeActor {} - - } -} diff --git a/src/Unit-1/CSharp/lesson3/Completed/Properties/AssemblyInfo.cs b/src/Unit-1/CSharp/lesson3/Completed/Properties/AssemblyInfo.cs deleted file mode 100644 index 6caf4a85a..000000000 --- a/src/Unit-1/CSharp/lesson3/Completed/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Lesson1-1")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Lesson1-1")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("60155426-eaa6-430b-bac5-1a0c7dc0cfcb")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Unit-1/CSharp/lesson3/Completed/ValidationActor.cs b/src/Unit-1/CSharp/lesson3/Completed/ValidationActor.cs deleted file mode 100644 index b245643e8..000000000 --- a/src/Unit-1/CSharp/lesson3/Completed/ValidationActor.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor that validates user input and signals result to others. - /// - public class ValidationActor : UntypedActor - { - private readonly ActorRef _consoleWriterActor; - - public ValidationActor(ActorRef consoleWriterActor) - { - _consoleWriterActor = consoleWriterActor; - } - - protected override void OnReceive(object message) - { - var msg = message as string; - if (string.IsNullOrEmpty(msg)) - { - // signal that the user needs to supply an input - _consoleWriterActor.Tell(new Messages.NullInputError("No input received.")); - } - else - { - var valid = IsValid(msg); - if (valid) - { - // send success to console writer - _consoleWriterActor.Tell(new Messages.InputSuccess("Thank you! Message was valid.")); - } - else - { - // signal that input was bad - _consoleWriterActor.Tell(new Messages.ValidationError("Invalid: input had odd number of characters.")); - } - } - - // tell sender to continue doing its thing (whatever that may be, this actor doesn't care) - Sender.Tell(new Messages.ContinueProcessing()); - - } - - /// - /// Determines if the message received is valid. - /// Currently, arbitrarily checks if number of chars in message received is even. - /// - /// - /// - private static bool IsValid(string msg) - { - var valid = msg.Length % 2 == 0; - return valid; - } - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson3/Completed/WinTail.csproj b/src/Unit-1/CSharp/lesson3/Completed/WinTail.csproj deleted file mode 100644 index 2a27f5030..000000000 --- a/src/Unit-1/CSharp/lesson3/Completed/WinTail.csproj +++ /dev/null @@ -1,70 +0,0 @@ - - - - - Debug - AnyCPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F} - Exe - Properties - WinTail - Lesson1-1 - v4.5 - 512 - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - packages\Akka.0.8.0\lib\net45\Akka.dll - - - False - packages\Newtonsoft.Json.6.0.1\lib\net45\Newtonsoft.Json.dll - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson3/Completed/WinTail.sln b/src/Unit-1/CSharp/lesson3/Completed/WinTail.sln deleted file mode 100644 index 1c0539943..000000000 --- a/src/Unit-1/CSharp/lesson3/Completed/WinTail.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinTail", "WinTail.csproj", "{E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/Unit-1/CSharp/lesson3/Completed/packages.config b/src/Unit-1/CSharp/lesson3/Completed/packages.config deleted file mode 100644 index 88dee0f6e..000000000 --- a/src/Unit-1/CSharp/lesson3/Completed/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson3/Images/working_lesson3.jpg b/src/Unit-1/CSharp/lesson3/Images/working_lesson3.jpg deleted file mode 100644 index 22ec84c8f..000000000 Binary files a/src/Unit-1/CSharp/lesson3/Images/working_lesson3.jpg and /dev/null differ diff --git a/src/Unit-1/CSharp/lesson3/README.md b/src/Unit-1/CSharp/lesson3/README.md deleted file mode 100644 index 6f3d03143..000000000 --- a/src/Unit-1/CSharp/lesson3/README.md +++ /dev/null @@ -1,422 +0,0 @@ -# Lesson 1.3: `Props` and `ActorRef`s -In this lesson, we will review/reinforce the different ways you can create actors and send them messages. This lesson is more conceptual and has less coding for you to do, but it's an essential foundation and key to understanding the code you will see down the line. - -In this lesson, the code has changed a bit. The change is that the `ConsoleReaderActor` no longer does any validation work, but instead, just passes off the messages it receives from the console to another actor for validation (the `ValidationActor`). - -## Key concepts / background -### `ActorRef`s -#### What is an `ActorRef`? -An `ActorRef` is a reference or handle to an actor. The purpose of an `ActorRef` is to support sending messages to an actor through the `ActorSystem`. You never talk directly to an actor—you send messages to its `ActorRef` and the `ActorSystem` takes care of delivering those messages for you. - -#### WTF? I don't actually talk to my actors? Why not? -You do talk to them, just not directly :) You have to talk to them via the intermediary of the `ActorSystem`. - -Here are two of the reasons why it is an advantage to send messages to an `ActorRef` and let the underlying `ActorSystem` do the work of getting the messages to the actual actor. - - It gives you better information to work with and messaging semantics. The `ActorSystem` wraps all messages in an `Envelope` that contains metadata about the message. This metadata is automatically unpacked and made available in the context of your actor. - - It allows "location transparency": this is a fancy way of saying that you don't have to worry about which process or machine your actor lives in. Keeping track of all this is the system's job. This is essential for allowing remote actors, which is how you can scale an actor system up to handle massive amounts of data (e.g. have it work on multiple machines in a cluster). More on this later. - -#### How do I know my message got delivered to the actor? -For now, this is not something you should worry about. The underlying `ActorSystem` of Akka.NET itself provides mechanisms to guarantee this, but `GuaranteedDeliveryActors` are an advanced topic. - -For now, just trust that delivering messages is the `ActorSystem`s job, not yours. Trust, baby. :) - -#### Okay, fine, I'll let the system deliver my messages. So how do I get an `ActorRef`? -There are two ways to get an `ActorRef`. - -##### 1) Create the actor -Actors form intrinsic supervision hierarchies (we cover in detail in lesson 5). This means there are "top level" actors, which essentially report directly to the `ActorSystem` itself, and there are "child" actors, which report to other actors. - -To make an actor, you have to create it from its context. And **you've already done this!** Remember this? -```csharp -// assume we have an existing actor system, "MyActorSystem" -ActorRef myFirstActor = MyActorSystem.ActorOf(Props.Create(() => new MyActorClass()), "myFirstActor") -``` - -As shown in the above example, you create an actor in the context of the actor that will supervise it (almost always). When you create the actor on the `ActorSystem` directly (as above), it is a top-level actor. - -You make child actors the same way, except you create them from another actor, like so: -```csharp -// have to create the child actor somewhere inside myFirstActor -// usually happens inside OnReceive or PreStart -class MyActorClass : UntypedActor{ - protected override void PreStart(){ - ActorRef myFirstChildActor = Context.ActorOf(Props.Create(() => new MyChildActorClass()), "myFirstChildActor") - } -} -``` - -**\*CAUTION***: Do NOT call `new MyActorClass()` outside of `Props` and the `ActorSystem` to make an actor. We can't go into all the details here, but essentially, by doing so you're trying to create an actor outside the context of the `ActorSystem`. This will produce a completely unusable, undesirable object. - - -##### 2) Look up the actor -All actors have an address (technically, an `ActorPath`) which represents where they are in the system hierarchy, and you can get a handle to them (get their `ActorRef`) by looking them up by their address. - -We will cover this in much more detail in the next lesson. - -#### Do I have to name my actors? -You may have noticed that we passed names into the `ActorSystem` when we were creating the above actors: -```csharp -// last arg to the call to ActorOf() if a name -ActorRef myFirstActor = MyActorSystem.ActorOf(Props.Create(() => new MyActorClass()), "myFirstActor") -``` - -This name is not required. It is perfectly valid to create an actor without a name, like so: -```csharp -// last arg to the call to ActorOf() if a name -ActorRef myFirstActor = MyActorSystem.ActorOf(Props.Create(() => new MyActorClass())) -``` - -That said, **the best practice is to name your actors**. Why? Because the name of your actor is used in log messages and in identifying actors. Get in the habit, and your future self will thank you when you have to debug something and it has a nice label on it :) - -#### Are there different types of `ActorRef`s? -Actually, yes. The most common, by far, is just a plain-old `ActorRef` or handle to an actor, as above. - -However, there are also some other `ActorRef`s available to you within the context of an actor. As we said, all actors have a context. That context holds metadata, which includes information about the current message being processed. That information includes things like the `Parent` or `Children` of the current actor, as well as the `Sender` of the current message. - -`Parent`, `Children`, and `Sender` all provide `ActorRef`s that you can use. - -### Props -#### What are `Props`? -Think of `Props` as a recipe for making an actor. Technically, `Props` is a configuration class that encapsulates all the information needed to make an instance of a given type of actor. - -#### Why do we need `Props`? -`Props` objects are shareable recipes for creating an instance of an actor. `Props` get passed to the `ActorSystem` to generate an actor for your use. - -Right now, `Props` probably feels like overkill. (If so, no worries.) But here's the deal. - -The most basic `Props`, like we've seen, seem to only include the ingredients needed to make an actor—it's class and required args to its constructor. - -BUT, what you haven't seen yet is that `Props` get extended to contain deployment information and other configuration details that are needed to do remote work. For example, `Props` are serializable can be used to remotely create and deploy entire groups of actors on another machine somewhere on the network! - -That's getting way ahead of ourselves though, but the short answer is that we need `Props` to support a lot of the advanced features (clustering, remote actors, etc) that give Akka.NET the serious horsepower which makes it interesting. - -#### How do I make `Props`? -Before we tell you how to make `Props`, let me tell you what NOT to do. - -***DO NOT TRY TO MAKE PROPS BY CALLING `new Props(...)`.*** Similar to trying to make an actor by calling `new MyActorClass()`, this is fighting the framework and not letting Akka's `ActorSystem` do its work under the hood to provide safe guarantees about actor restarts and lifecycle management. - -There are 3 ways to properly create `Props`, and they all involve a call to `Props.Create()`. - -1. **The `typeof` syntax:** - ```csharp - Props props1 = Props.Create(typeof(MyActor)); - ``` - - While it looks simple, **we recommend that you do not use this approach.** Why? *Because it has no type safety and can easily introduce bugs where everything compiles fine, and then blows up at runtime*. - -1. **The lambda syntax**: - ```csharp - Props props2 = Props.Create(() => new MyActor(..), "..."); - ``` - - This is a mighty fine syntax, and our favorite. You can pass in the arguments required by the constructor of your actor class inline, along with a name. - -1. **The generic syntax**: - ```csharp - Props props3 = Props.Create(); - ``` - - Another fine syntax that we whole-heartedly recommend. - -#### How do I use `Props`? -You actually already know this, and have done it. You pass the `Props`—the actor recipe—to the call to `Context.ActorOf()` and the underlying `ActorSystem` reads the recipe, et voila! Whips you up a fresh new actor. - -Enough of this conceptual business. Let's get to it! - -## Exercise -Before we can get into the meat of this lesson (`Props` and `ActorRef`s), we have to do a bit of cleanup. - -### Phase 1: Move validation into its own actor -We're going to move all our validation code into its own actor. It really doesn't belong in the `ConsoleReaderActor`. Validation deserves to have its own actor (similar to how you want single-purpose objects in OOP). - -#### Create `ValidationActor` class -Make a new class called `ValidationActor` and put it into its own file. Fill it with all the validation logic that is currently in `ConsoleReaderActor`: - -```csharp -// ValidationActor.cs -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor that validates user input and signals result to others. - /// - public class ValidationActor : UntypedActor - { - private readonly ActorRef _consoleWriterActor; - - public ValidationActor(ActorRef consoleWriterActor) - { - _consoleWriterActor = consoleWriterActor; - } - - protected override void OnReceive(object message) - { - var msg = message as string; - if (string.IsNullOrEmpty(msg)) - { - // signal that the user needs to supply an input - _consoleWriterActor.Tell(new Messages.NullInputError("No input received.")); - } - else - { - var valid = IsValid(msg); - if (valid) - { - // send success to console writer - _consoleWriterActor.Tell(new Messages.InputSuccess("Thank you! Message was valid.")); - } - else - { - // signal that input was bad - _consoleWriterActor.Tell(new Messages.ValidationError("Invalid: input had odd number of characters.")); - } - } - - // tell sender to continue doing its thing (whatever that may be, this actor doesn't care) - Sender.Tell(new Messages.ContinueProcessing()); - - } - - /// - /// Determines if the message received is valid. - /// Currently, arbitrarily checks if number of chars in message received is even. - /// - /// - /// - private static bool IsValid(string msg) - { - var valid = msg.Length % 2 == 0; - return valid; - } - } -} -``` - -### Phase 2: Making `Props`, our actor recipes -Okay, now we can get to the good stuff! We are going to use what we've learned about `Props` and tweak the way we make our actors. - -Again, we do not recommend using the `typeof` syntax. For practice, use both of the lambda and generic syntax! - -> **Remember**: do NOT try to create `Props` by calling `new Props(...)`. -> -> When you do that, kittens die, unicorns vanish, Mordor wins and all manner of badness happens. Let's just not. - -In this section, we're going to split out the `Props` objects onto their own lines for easier reading. In practice, we usually inline them into the call to `ActorOf`. - -#### Delete existing `Props` and `ActorRef`s -In `Main()`, remove your existing actor declarations so we have a clean slate. - -Your code should look like this right now: - -```csharp -// Program.cs -static void Main(string[] args) -{ - // initialize MyActorSystem - MyActorSystem = ActorSystem.Create("MyActorSystem"); - - // nothing here where our actors used to be! - - // tell console reader to begin - consoleReaderActor.Tell(ConsoleReaderActor.StartCommand); - - // blocks the main thread from exiting until the actor system is shut down - MyActorSystem.AwaitTermination(); -} -``` - - -#### Make `consoleWriterProps` -Go to `Program.cs`. Inside of `Main()`, split out `consoleWriterProps` onto its own line like so: - -```csharp -// Program.cs -Props consoleWriterProps = Props.Create(typeof (ConsoleWriterActor)); -``` - -Here you can see we're using the typeof syntax, just to show you what it's like. But again, *we do not recommend using the `typeof` syntax in practice*. - -Going forward, we'll only use the lambda and generic syntaxes for `Props`. - -#### Make `validationActorProps` -Add this just to `Main()` also: - -```csharp -// Program.cs -Props validationActorProps = Props.Create(() => new ValidationActor(consoleWriterActor)); -``` - -As you can see, here we're using the lambda syntax. - -#### Make `consoleReaderProps` -Add this just to `Main()` also: - -```csharp -// Program.cs -Props consoleReaderProps = Props.Create(validationActor); -``` - -This is the generic syntax. `Props` accepts the actor class as a generic type argument, and then we pass in whatever the actor's constructor needs. - -### Phase 3: Making `ActorRef`s using various `Props` -Great! Now that we've got `Props` for all the actors we want, let's go make some actors! - -Remember: do not try to make an actor by calling `new Actor()` outside of a `Props` object and/or outside the context of the `ActorSystem` or another `ActorRef`. Mordor and all that, remember? - -#### Make a new `ActorRef` for `consoleWriterActor` -Add this to `Main()` on the line after `consoleWriterProps`: -```csharp -// Program.cs -ActorRef consoleWriterActor = MyActorSystem.ActorOf(consoleWriterProps, "consoleWriterActor"); -``` - - -#### Make a new `ActorRef` for `validationActor` -Add this to `Main()` on the line after `validationActorProps`: - -```csharp -// Program.cs -ActorRef validationActor = MyActorSystem.ActorOf(validationActorProps, "validationActor"); -``` - -#### Make a new `ActorRef` for `consoleReaderActor` -Add this to `Main()` on the line after `consoleReaderProps`: - -```csharp -// Program.cs -ActorRef consoleReaderActor = MyActorSystem.ActorOf(consoleReaderProps, "consoleReaderActor"); -``` - -#### Calling out a special `ActorRef`: `Sender` -You may not have noticed it, but we actually are using a special `ActorRef` now: `Sender`. Go look for this in `ValidationActor.cs`: - -```csharp -// tell sender to continue doing its thing (whatever that may be, this actor doesn't care) -Sender.Tell(new Messages.ContinueProcessing()); -``` - -This is the special `Sender` handle that is made available within an actors `Context` when it is processing a message. The `Context` always makes this reference available, along with some other metadata (more on that later). - -### Phase 4: A bit of cleanup -Just a bit of cleanup since we've changed our class structure. Then we can run our app again! - -#### Update `ConsoleReaderActor` -Now that `ValidationActor` is doing our validation work, we should really slim down `ConsoleReaderActor`. Let's clean it up and have it just hand the message off to the `ValidationActor` for validation. - -We'll also need to store a reference to `ValidationActor` inside the `ConsoleReaderActor`, and we don't need a reference to the the `ConsoleWriterActor` anymore, so let's do some cleanup. - -Modify your version of `ConsoleReaderActor` to match the below: - -```csharp -// ConsoleReaderActor.cs -// removing validation logic and changing store actor references -using System; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor responsible for reading FROM the console. - /// Also responsible for calling . - /// - class ConsoleReaderActor : UntypedActor - { - public const string StartCommand = "start"; - public const string ExitCommand = "exit"; - private readonly ActorRef _validationActor; - - public ConsoleReaderActor(ActorRef validationActor) - { - _validationActor = validationActor; - } - - protected override void OnReceive(object message) - { - if (message.Equals(StartCommand)) - { - DoPrintInstructions(); - } - - GetAndValidateInput(); - } - - - #region Internal methods - private void DoPrintInstructions() - { - Console.WriteLine("Write whatever you want into the console!"); - Console.WriteLine("Some entries will pass validation, and some won't...\n\n"); - Console.WriteLine("Type 'exit' to quit this application at any time.\n"); - } - - - /// - /// Reads input from console, validates it, then signals appropriate response - /// (continue processing, error, success, etc.). - /// - private void GetAndValidateInput() - { - var message = Console.ReadLine(); - if (!string.IsNullOrEmpty(message) && String.Equals(message, ExitCommand, StringComparison.OrdinalIgnoreCase)) - { - // if user typed ExitCommand, shut down the entire actor system (allows the process to exit) - Context.System.Shutdown(); - return; - } - - // otherwise, just hand message off to validation actor (by telling its actor ref) - _validationActor.Tell(message); - } - #endregion - } -} - -``` - -As you can see, we're now handing off the input from the console to the `ValidationActor` for validation and decisions. `ConsoleReaderActor` is now only responsible for reading from the console and handing the data off to another more sophisticated actor. - -#### Fix that first `Props` call... -We can't very well recommend you not use the `typeof` syntax and then let it stay there. Real quick, go back to `Main()` and update `consoleWriterProps` to be use the generic syntax. - -```csharp -Props consoleWriterProps = Props.Create(); -``` - -There. That's better. - -### Once you're done -Compare your code to the solution in the [Completed](Completed/) folder to see what the instructors included in their samples. - -If everything is working as it should, the output you see should be identical to last time: -![Petabridge Akka.NET Bootcamp Lesson 1.2 Correct Output](Images/working_lesson3.jpg) - - -#### Experience the danger of the `typeof` syntax for `Props` yourself -Since we harped on it earlier, let's illustrate the risk of using the `typeof` `Props` syntax and why we avoid it. - -We've left a little landmine as a demonstration. You should blow it up just to see what happens. - -1. Open up [Completed/WinTail.sln](Completed/WinTail.sln). -1. Find the lines containing `fakeActorProps` and `fakeActor` (should be around line 18). -2. Uncomment these lines. - - Look at what we're doing here—intentionally substituting a non-actor class into a `Props` object! Ridiculous! Terrible! - - While this is an unlikely and frankly ridiculous example, that is exactly the point. It's just leaving open the door for mistakes, even with good intentions. -1. Build the solution. Watch with horror as this ridiculous piece of code *compiles without error!* -1. Run the solution. -1. Try to shield yourself from everything melting down when your program reaches that line of code. - -Okay, so what was the point of that? Contrived as that example was, it should show you that *using the `typeof` syntax for `Props` has no type safety and is best avoided unless you have a damn good reason to use it.* - -## Great job! Onto Lesson 4! -Awesome work! Well done on completing your this lesson. It was a big one. - -**Let's move onto [Lesson 4 - Child Actors, Actor Hierarchies, and Supervision](../lesson4).** - -## Any questions? -**Don't be afraid to ask questions** :). - -Come ask any questions you have, big or small, [in this ongoing Bootcamp chat with the Petabridge & Akka.NET teams](https://gitter.im/petabridge/akka-bootcamp). - -### Problems with the code? -If there is a problem with the code running, or something else that needs to be fixed in this lesson, please [create an issue](/issues) and we'll get right on it. This will benefit everyone going through Bootcamp. diff --git a/src/Unit-1/CSharp/lesson4/Completed/App.config b/src/Unit-1/CSharp/lesson4/Completed/App.config deleted file mode 100644 index 8e1564635..000000000 --- a/src/Unit-1/CSharp/lesson4/Completed/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson4/Completed/ConsoleReaderActor.cs b/src/Unit-1/CSharp/lesson4/Completed/ConsoleReaderActor.cs deleted file mode 100644 index 142ce7767..000000000 --- a/src/Unit-1/CSharp/lesson4/Completed/ConsoleReaderActor.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor responsible for reading FROM the console. - /// Also responsible for calling . - /// - class ConsoleReaderActor : UntypedActor - { - public const string StartCommand = "start"; - public const string ExitCommand = "exit"; - private readonly ActorRef _validationActor; - - public ConsoleReaderActor(ActorRef validationActor) - { - _validationActor = validationActor; - } - - protected override void OnReceive(object message) - { - if (message.Equals(StartCommand)) - { - DoPrintInstructions(); - } - - GetAndValidateInput(); - } - - - #region Internal methods - private void DoPrintInstructions() - { - Console.WriteLine("Please provide the URI of a log file on disk.\n"); - } - - - /// - /// Reads input from console, validates it, then signals appropriate response - /// (continue processing, error, success, etc.). - /// - private void GetAndValidateInput() - { - var message = Console.ReadLine(); - if (!string.IsNullOrEmpty(message) && String.Equals(message, ExitCommand, StringComparison.OrdinalIgnoreCase)) - { - // if user typed ExitCommand, shut down the entire actor system (allows the process to exit) - Context.System.Shutdown(); - return; - } - - // otherwise, just hand message off to validation actor (by telling its actor ref) - _validationActor.Tell(message); - } - #endregion - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson4/Completed/ConsoleWriterActor.cs b/src/Unit-1/CSharp/lesson4/Completed/ConsoleWriterActor.cs deleted file mode 100644 index 05efe1a08..000000000 --- a/src/Unit-1/CSharp/lesson4/Completed/ConsoleWriterActor.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor responsible for serializing message writes to the console. - /// (write one message at a time, champ :) - /// - class ConsoleWriterActor : UntypedActor - { - protected override void OnReceive(object message) - { - if (message is Messages.InputError) - { - var msg = message as Messages.InputError; - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine(msg.Reason); - } - else if (message is Messages.InputSuccess) - { - var msg = message as Messages.InputSuccess; - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine(msg.Reason); - } - else - { - Console.WriteLine(message); - } - - Console.ResetColor(); - } - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson4/Completed/FileObserver.cs b/src/Unit-1/CSharp/lesson4/Completed/FileObserver.cs deleted file mode 100644 index 398ab8419..000000000 --- a/src/Unit-1/CSharp/lesson4/Completed/FileObserver.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System; -using System.IO; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Turns events about a specific file into messages for . - /// - public class FileObserver : IDisposable - { - private readonly ActorRef _tailActor; - private readonly string _absoluteFilePath; - private FileSystemWatcher _watcher; - private readonly string _fileDir; - private readonly string _fileNameOnly; - - public FileObserver(ActorRef tailActor, string absoluteFilePath) - { - _tailActor = tailActor; - _absoluteFilePath = absoluteFilePath; - _fileDir = Path.GetDirectoryName(absoluteFilePath); - _fileNameOnly = Path.GetFileName(absoluteFilePath); - } - - /// - /// Begin monitoring file. - /// - public void Start() - { - // Need this for Mono 3.12.0 workaround - // uncomment this line if you're running on Mono! - // Environment.SetEnvironmentVariable("MONO_MANAGED_WATCHER", "enabled"); - - // make watcher to observe our specific file - _watcher = new FileSystemWatcher(_fileDir, _fileNameOnly); - - // watch our file for changes to the file name, or new messages being written to file - _watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite; - - // assign callbacks for event types - _watcher.Changed += OnFileChanged; - _watcher.Error += OnFileError; - - // start watching - _watcher.EnableRaisingEvents = true; - - } - - /// - /// Stop monitoring file. - /// - public void Dispose() - { - _watcher.Dispose(); - } - - /// - /// Callback for file error events. - /// - /// - /// - void OnFileError(object sender, ErrorEventArgs e) - { - _tailActor.Tell(new TailActor.FileError(_fileNameOnly, e.GetException().Message), ActorRef.NoSender); - } - - /// - /// Callback for file change events. - /// - /// - /// - void OnFileChanged(object sender, FileSystemEventArgs e) - { - if (e.ChangeType == WatcherChangeTypes.Changed) - { - // here we use a special ActorRef.NoSender - // since this event can happen many times, this is a little microoptimization - _tailActor.Tell(new TailActor.FileWrite(e.Name), ActorRef.NoSender); - } - - } - - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson4/Completed/FileValidatorActor.cs b/src/Unit-1/CSharp/lesson4/Completed/FileValidatorActor.cs deleted file mode 100644 index 4dc9a13f3..000000000 --- a/src/Unit-1/CSharp/lesson4/Completed/FileValidatorActor.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System.IO; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor that validates user input and signals result to others. - /// - public class FileValidatorActor : UntypedActor - { - private readonly ActorRef _consoleWriterActor; - private readonly ActorRef _tailCoordinatorActor; - - public FileValidatorActor(ActorRef consoleWriterActor, ActorRef tailCoordinatorActor) - { - _consoleWriterActor = consoleWriterActor; - _tailCoordinatorActor = tailCoordinatorActor; - } - - protected override void OnReceive(object message) - { - var msg = message as string; - if (string.IsNullOrEmpty(msg)) - { - // signal that the user needs to supply an input - _consoleWriterActor.Tell(new Messages.NullInputError("Input was blank. Please try again.\n")); - - // tell sender to continue doing its thing (whatever that may be, this actor doesn't care) - Sender.Tell(new Messages.ContinueProcessing()); - } - else - { - var valid = IsFileUri(msg); - if (valid) - { - // signal successful input - _consoleWriterActor.Tell(new Messages.InputSuccess(string.Format("Starting processing for {0}", msg))); - - // start coordinator - _tailCoordinatorActor.Tell(new TailCoordinatorActor.StartTail(msg, _consoleWriterActor)); - } - else - { - // signal that input was bad - _consoleWriterActor.Tell(new Messages.ValidationError(string.Format("{0} is not an existing URI on disk.", msg))); - - // tell sender to continue doing its thing (whatever that may be, this actor doesn't care) - Sender.Tell(new Messages.ContinueProcessing()); - } - } - - - - } - - /// - /// Checks if file exists at path provided by user. - /// - /// - /// - private static bool IsFileUri(string path) - { - return File.Exists(path); - } - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson4/Completed/Messages.cs b/src/Unit-1/CSharp/lesson4/Completed/Messages.cs deleted file mode 100644 index f322a76ed..000000000 --- a/src/Unit-1/CSharp/lesson4/Completed/Messages.cs +++ /dev/null @@ -1,59 +0,0 @@ -namespace WinTail -{ - class Messages - { - - #region Neutral/system messages - /// - /// Marker class to continue processing. - /// - public class ContinueProcessing { } - #endregion - - #region Success messages - /// - /// Base class for signalling that user input was valid. - /// - public class InputSuccess - { - public InputSuccess(string reason) - { - Reason = reason; - } - - public string Reason { get; private set; } - } - #endregion - - #region Error messages - /// - /// Base class for signalling that user input was invalid. - /// - public class InputError - { - public InputError(string reason) - { - Reason = reason; - } - - public string Reason { get; private set; } - } - - /// - /// User provided blank input. - /// - public class NullInputError : InputError - { - public NullInputError(string reason) : base(reason) { } - } - - /// - /// User provided invalid input (currently, input w/ odd # chars) - /// - public class ValidationError : InputError - { - public ValidationError(string reason) : base(reason) { } - } - #endregion - } -} diff --git a/src/Unit-1/CSharp/lesson4/Completed/Program.cs b/src/Unit-1/CSharp/lesson4/Completed/Program.cs deleted file mode 100644 index d73634cdd..000000000 --- a/src/Unit-1/CSharp/lesson4/Completed/Program.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Akka.Actor; - -namespace WinTail -{ - class Program - { - public static ActorSystem MyActorSystem; - - static void Main(string[] args) - { - // make actor system - MyActorSystem = ActorSystem.Create("MyActorSystem"); - - // create top-level actors within the actor system - Props consoleWriterProps = Props.Create(); - ActorRef consoleWriterActor = MyActorSystem.ActorOf(consoleWriterProps, "consoleWriterActor"); - - Props tailCoordinatorProps = Props.Create(() => new TailCoordinatorActor()); - ActorRef tailCoordinatorActor = MyActorSystem.ActorOf(tailCoordinatorProps, "tailCoordinatorActor"); - - Props fileValidatorActorProps = Props.Create(() => new FileValidatorActor(consoleWriterActor, tailCoordinatorActor)); - ActorRef fileValidatorActor = MyActorSystem.ActorOf(fileValidatorActorProps, "validationActor"); - - Props consoleReaderProps = Props.Create(fileValidatorActor); - ActorRef consoleReaderActor = MyActorSystem.ActorOf(consoleReaderProps, "consoleReaderActor"); - - // begin processing - consoleReaderActor.Tell(ConsoleReaderActor.StartCommand); - - // blocks the main thread from exiting until the actor system is shut down - MyActorSystem.AwaitTermination(); - } - - } -} diff --git a/src/Unit-1/CSharp/lesson4/Completed/Properties/AssemblyInfo.cs b/src/Unit-1/CSharp/lesson4/Completed/Properties/AssemblyInfo.cs deleted file mode 100644 index 6caf4a85a..000000000 --- a/src/Unit-1/CSharp/lesson4/Completed/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Lesson1-1")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Lesson1-1")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("60155426-eaa6-430b-bac5-1a0c7dc0cfcb")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Unit-1/CSharp/lesson4/Completed/TailActor.cs b/src/Unit-1/CSharp/lesson4/Completed/TailActor.cs deleted file mode 100644 index 099d717a2..000000000 --- a/src/Unit-1/CSharp/lesson4/Completed/TailActor.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System.IO; -using System.Text; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Monitors the file at for changes and sends file updates to console. - /// - public class TailActor : UntypedActor - { - #region Message types - - /// - /// Signal that the file has changed, and we need to read the next line of the file. - /// - public class FileWrite - { - public FileWrite(string fileName) - { - FileName = fileName; - } - - public string FileName { get; private set; } - } - - /// - /// Signal that the OS had an error accessing the file. - /// - public class FileError - { - public FileError(string fileName, string reason) - { - FileName = fileName; - Reason = reason; - } - - public string FileName { get; private set; } - - public string Reason { get; private set; } - } - - /// - /// Signal to read the initial contents of the file at actor startup. - /// - public class InitialRead - { - public InitialRead(string fileName, string text) - { - FileName = fileName; - Text = text; - } - - public string FileName { get; private set; } - public string Text { get; private set; } - } - - #endregion - - private readonly string _filePath; - private readonly ActorRef _reporterActor; - private readonly FileObserver _observer; - private readonly Stream _fileStream; - private readonly StreamReader _fileStreamReader; - - public TailActor(ActorRef reporterActor, string filePath) - { - _reporterActor = reporterActor; - _filePath = filePath; - - // start watching file for changes - _observer = new FileObserver(Self, Path.GetFullPath(_filePath)); - _observer.Start(); - - // open the file stream with shared read/write permissions (so file can be written to while open) - _fileStream = new FileStream(Path.GetFullPath(_filePath), FileMode.Open, FileAccess.Read, - FileShare.ReadWrite); - _fileStreamReader = new StreamReader(_fileStream, Encoding.UTF8); - - // read the initial contents of the file and send it to console as first message - var text = _fileStreamReader.ReadToEnd(); - Self.Tell(new InitialRead(_filePath, text)); - } - - protected override void OnReceive(object message) - { - if (message is FileWrite) - { - // move file cursor forward - // pull results from cursor to end of file and write to output - // (tis is assuming a log file type format that is append-only) - var text = _fileStreamReader.ReadToEnd(); - if (!string.IsNullOrEmpty(text)) - { - _reporterActor.Tell(text); - } - - } - else if (message is FileError) - { - var fe = message as FileError; - _reporterActor.Tell(string.Format("Tail error: {0}", fe.Reason)); - } - else if (message is InitialRead) - { - var ir = message as InitialRead; - _reporterActor.Tell(ir.Text); - } - } - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson4/Completed/TailCoordinatorActor.cs b/src/Unit-1/CSharp/lesson4/Completed/TailCoordinatorActor.cs deleted file mode 100644 index c1ee2e0c7..000000000 --- a/src/Unit-1/CSharp/lesson4/Completed/TailCoordinatorActor.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using Akka.Actor; - -namespace WinTail -{ - public class TailCoordinatorActor : UntypedActor - { - #region Message types - /// - /// Start tailing the file at user-specified path. - /// - public class StartTail - { - public StartTail(string filePath, ActorRef reporterActor) - { - FilePath = filePath; - ReporterActor = reporterActor; - } - - public string FilePath { get; private set; } - - public ActorRef ReporterActor { get; private set; } - } - - /// - /// Stop tailing the file at user-specified path. - /// - public class StopTail - { - public StopTail(string filePath) - { - FilePath = filePath; - } - - public string FilePath { get; private set; } - } - - #endregion - - protected override void OnReceive(object message) - { - if (message is StartTail) - { - var msg = message as StartTail; - Context.ActorOf(Props.Create(() => new TailActor(msg.ReporterActor, msg.FilePath))); - } - - } - - // here we are overriding the default SupervisorStrategy - // which is a One-For-One strategy w/ a Restart directive - protected override SupervisorStrategy SupervisorStrategy() - { - return new OneForOneStrategy ( - 10, // maxNumberOfRetries - TimeSpan.FromSeconds(30), // duration - decider: x => - { - //Maybe we consider ArithmeticException to not be application critical - //so we just ignore the error and keep going. - if (x is ArithmeticException) return Directive.Resume; - - //Error that we cannot recover from, stop the failing actor - else if (x is NotSupportedException) return Directive.Stop; - - //In all other cases, just restart the failing actor - else return Directive.Restart; - }); - } - } -} - - diff --git a/src/Unit-1/CSharp/lesson4/Completed/WinTail.csproj b/src/Unit-1/CSharp/lesson4/Completed/WinTail.csproj deleted file mode 100644 index 8550da4db..000000000 --- a/src/Unit-1/CSharp/lesson4/Completed/WinTail.csproj +++ /dev/null @@ -1,73 +0,0 @@ - - - - - Debug - AnyCPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F} - Exe - Properties - WinTail - Lesson1-1 - v4.5 - 512 - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - packages\Akka.0.8.0\lib\net45\Akka.dll - - - False - packages\Newtonsoft.Json.6.0.1\lib\net45\Newtonsoft.Json.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson4/Completed/WinTail.sln b/src/Unit-1/CSharp/lesson4/Completed/WinTail.sln deleted file mode 100644 index 1c0539943..000000000 --- a/src/Unit-1/CSharp/lesson4/Completed/WinTail.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinTail", "WinTail.csproj", "{E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/Unit-1/CSharp/lesson4/Completed/packages.config b/src/Unit-1/CSharp/lesson4/Completed/packages.config deleted file mode 100644 index 88dee0f6e..000000000 --- a/src/Unit-1/CSharp/lesson4/Completed/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson4/DoThis/FileObserver.cs b/src/Unit-1/CSharp/lesson4/DoThis/FileObserver.cs deleted file mode 100644 index 30156323a..000000000 --- a/src/Unit-1/CSharp/lesson4/DoThis/FileObserver.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.IO; -using Akka.Actor; - -namespace buildup -{ - /// - /// Turns events about a specific file into messages for . - /// - public class FileObserver : IDisposable - { - private readonly ActorRef _tailActor; - private readonly string _absoluteFilePath; - private FileSystemWatcher _watcher; - private readonly string _fileDir; - private readonly string _fileNameOnly; - - public FileObserver(ActorRef tailActor, string absoluteFilePath) - { - _tailActor = tailActor; - _absoluteFilePath = absoluteFilePath; - _fileDir = Path.GetDirectoryName(absoluteFilePath); - _fileNameOnly = Path.GetFileName(absoluteFilePath); - } - - /// - /// Begin monitoring file. - /// - public void Start() - { - // make watcher to observe our specific file - _watcher = new FileSystemWatcher(_fileDir, _fileNameOnly); - - // watch our file for changes to the file name, or new messages being written to file - _watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite; - - // assign callbacks for event types - _watcher.Changed += OnFileChanged; - _watcher.Error += OnFileError; - - // start watching - _watcher.EnableRaisingEvents = true; - - } - - /// - /// Stop monitoring file. - /// - public void Dispose() - { - _watcher.Dispose(); - } - - /// - /// Callback for file error events. - /// - /// - /// - void OnFileError(object sender, ErrorEventArgs e) - { - _tailActor.Tell(new TailActor.FileError(_fileNameOnly, e.GetException().Message), ActorRef.NoSender); - } - - /// - /// Callback for file change events. - /// - /// - /// - void OnFileChanged(object sender, FileSystemEventArgs e) - { - if (e.ChangeType == WatcherChangeTypes.Changed) - { - // here we use a special ActorRef.NoSender - // since this event can happen many times, this is a little microoptimization - _tailActor.Tell(new TailActor.FileWrite(e.Name), ActorRef.NoSender); - } - - } - - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson4/DoThis/TailActor.cs b/src/Unit-1/CSharp/lesson4/DoThis/TailActor.cs deleted file mode 100644 index 099d717a2..000000000 --- a/src/Unit-1/CSharp/lesson4/DoThis/TailActor.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System.IO; -using System.Text; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Monitors the file at for changes and sends file updates to console. - /// - public class TailActor : UntypedActor - { - #region Message types - - /// - /// Signal that the file has changed, and we need to read the next line of the file. - /// - public class FileWrite - { - public FileWrite(string fileName) - { - FileName = fileName; - } - - public string FileName { get; private set; } - } - - /// - /// Signal that the OS had an error accessing the file. - /// - public class FileError - { - public FileError(string fileName, string reason) - { - FileName = fileName; - Reason = reason; - } - - public string FileName { get; private set; } - - public string Reason { get; private set; } - } - - /// - /// Signal to read the initial contents of the file at actor startup. - /// - public class InitialRead - { - public InitialRead(string fileName, string text) - { - FileName = fileName; - Text = text; - } - - public string FileName { get; private set; } - public string Text { get; private set; } - } - - #endregion - - private readonly string _filePath; - private readonly ActorRef _reporterActor; - private readonly FileObserver _observer; - private readonly Stream _fileStream; - private readonly StreamReader _fileStreamReader; - - public TailActor(ActorRef reporterActor, string filePath) - { - _reporterActor = reporterActor; - _filePath = filePath; - - // start watching file for changes - _observer = new FileObserver(Self, Path.GetFullPath(_filePath)); - _observer.Start(); - - // open the file stream with shared read/write permissions (so file can be written to while open) - _fileStream = new FileStream(Path.GetFullPath(_filePath), FileMode.Open, FileAccess.Read, - FileShare.ReadWrite); - _fileStreamReader = new StreamReader(_fileStream, Encoding.UTF8); - - // read the initial contents of the file and send it to console as first message - var text = _fileStreamReader.ReadToEnd(); - Self.Tell(new InitialRead(_filePath, text)); - } - - protected override void OnReceive(object message) - { - if (message is FileWrite) - { - // move file cursor forward - // pull results from cursor to end of file and write to output - // (tis is assuming a log file type format that is append-only) - var text = _fileStreamReader.ReadToEnd(); - if (!string.IsNullOrEmpty(text)) - { - _reporterActor.Tell(text); - } - - } - else if (message is FileError) - { - var fe = message as FileError; - _reporterActor.Tell(string.Format("Tail error: {0}", fe.Reason)); - } - else if (message is InitialRead) - { - var ir = message as InitialRead; - _reporterActor.Tell(ir.Text); - } - } - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson4/DoThis/TailCoordinatorActor.cs b/src/Unit-1/CSharp/lesson4/DoThis/TailCoordinatorActor.cs deleted file mode 100644 index 479db51bc..000000000 --- a/src/Unit-1/CSharp/lesson4/DoThis/TailCoordinatorActor.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Akka.Actor; - -namespace WinTail -{ - public class TailCoordinatorActor : UntypedActor - { - #region Message types - /// - /// Start tailing the file at user-specified path. - /// - public class StartTail - { - public StartTail(string filePath, ActorRef reporterActor) - { - FilePath = filePath; - ReporterActor = reporterActor; - } - - public string FilePath { get; private set; } - - public ActorRef ReporterActor { get; private set; } - } - - /// - /// Stop tailing the file at user-specified path. - /// - public class StopTail - { - public StopTail(string filePath) - { - FilePath = filePath; - } - - public string FilePath { get; private set; } - } - - #endregion - - protected override void OnReceive(object message) - { - if (message is StartTail) - { - var msg = message as StartTail; - // YOU NEED TO FILL IN HERE - } - - } - } -} - - diff --git a/src/Unit-1/CSharp/lesson4/DoThis/sample_log_file.txt b/src/Unit-1/CSharp/lesson4/DoThis/sample_log_file.txt deleted file mode 100644 index b6a9efb78..000000000 --- a/src/Unit-1/CSharp/lesson4/DoThis/sample_log_file.txt +++ /dev/null @@ -1,50 +0,0 @@ -64.242.88.10 - - [07/Mar/2004:16:05:49 -0800] "GET /twiki/bin/edit/Main/Double_bounce_sender?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:16:06:51 -0800] "GET /twiki/bin/rdiff/TWiki/NewUserTemplate?rev1=1.3&rev2=1.2 HTTP/1.1" 200 4523 -64.242.88.10 - - [07/Mar/2004:16:10:02 -0800] "GET /mailman/listinfo/hsdivision HTTP/1.1" 200 6291 -64.242.88.10 - - [07/Mar/2004:16:11:58 -0800] "GET /twiki/bin/view/TWiki/WikiSyntax HTTP/1.1" 200 7352 -64.242.88.10 - - [07/Mar/2004:16:20:55 -0800] "GET /twiki/bin/view/Main/DCCAndPostFix HTTP/1.1" 200 5253 -64.242.88.10 - - [07/Mar/2004:16:23:12 -0800] "GET /twiki/bin/oops/TWiki/AppendixFileSystem?template=oopsmore¶m1=1.12¶m2=1.12 HTTP/1.1" 200 11382 -64.242.88.10 - - [07/Mar/2004:16:24:16 -0800] "GET /twiki/bin/view/Main/PeterThoeny HTTP/1.1" 200 4924 -64.242.88.10 - - [07/Mar/2004:16:29:16 -0800] "GET /twiki/bin/edit/Main/Header_checks?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12851 -64.242.88.10 - - [07/Mar/2004:16:30:29 -0800] "GET /twiki/bin/attach/Main/OfficeLocations HTTP/1.1" 401 12851 -64.242.88.10 - - [07/Mar/2004:16:31:48 -0800] "GET /twiki/bin/view/TWiki/WebTopicEditTemplate HTTP/1.1" 200 3732 -64.242.88.10 - - [07/Mar/2004:16:32:50 -0800] "GET /twiki/bin/view/Main/WebChanges HTTP/1.1" 200 40520 -64.242.88.10 - - [07/Mar/2004:16:33:53 -0800] "GET /twiki/bin/edit/Main/Smtpd_etrn_restrictions?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12851 -64.242.88.10 - - [07/Mar/2004:16:35:19 -0800] "GET /mailman/listinfo/business HTTP/1.1" 200 6379 -64.242.88.10 - - [07/Mar/2004:16:36:22 -0800] "GET /twiki/bin/rdiff/Main/WebIndex?rev1=1.2&rev2=1.1 HTTP/1.1" 200 46373 -64.242.88.10 - - [07/Mar/2004:16:37:27 -0800] "GET /twiki/bin/view/TWiki/DontNotify HTTP/1.1" 200 4140 -64.242.88.10 - - [07/Mar/2004:16:39:24 -0800] "GET /twiki/bin/view/Main/TokyoOffice HTTP/1.1" 200 3853 -64.242.88.10 - - [07/Mar/2004:16:43:54 -0800] "GET /twiki/bin/view/Main/MikeMannix HTTP/1.1" 200 3686 -64.242.88.10 - - [07/Mar/2004:16:45:56 -0800] "GET /twiki/bin/attach/Main/PostfixCommands HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:16:47:12 -0800] "GET /robots.txt HTTP/1.1" 200 68 -64.242.88.10 - - [07/Mar/2004:16:47:46 -0800] "GET /twiki/bin/rdiff/Know/ReadmeFirst?rev1=1.5&rev2=1.4 HTTP/1.1" 200 5724 -64.242.88.10 - - [07/Mar/2004:16:49:04 -0800] "GET /twiki/bin/view/Main/TWikiGroups?rev=1.2 HTTP/1.1" 200 5162 -64.242.88.10 - - [07/Mar/2004:16:50:54 -0800] "GET /twiki/bin/rdiff/Main/ConfigurationVariables HTTP/1.1" 200 59679 -64.242.88.10 - - [07/Mar/2004:16:52:35 -0800] "GET /twiki/bin/edit/Main/Flush_service_name?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12851 -64.242.88.10 - - [07/Mar/2004:16:53:46 -0800] "GET /twiki/bin/rdiff/TWiki/TWikiRegistration HTTP/1.1" 200 34395 -64.242.88.10 - - [07/Mar/2004:16:54:55 -0800] "GET /twiki/bin/rdiff/Main/NicholasLee HTTP/1.1" 200 7235 -64.242.88.10 - - [07/Mar/2004:16:56:39 -0800] "GET /twiki/bin/view/Sandbox/WebHome?rev=1.6 HTTP/1.1" 200 8545 -64.242.88.10 - - [07/Mar/2004:16:58:54 -0800] "GET /mailman/listinfo/administration HTTP/1.1" 200 6459 -lordgun.org - - [07/Mar/2004:17:01:53 -0800] "GET /razor.html HTTP/1.1" 200 2869 -64.242.88.10 - - [07/Mar/2004:17:09:01 -0800] "GET /twiki/bin/search/Main/SearchResult?scope=text®ex=on&search=Joris%20*Benschop[^A-Za-z] HTTP/1.1" 200 4284 -64.242.88.10 - - [07/Mar/2004:17:10:20 -0800] "GET /twiki/bin/oops/TWiki/TextFormattingRules?template=oopsmore¶m1=1.37¶m2=1.37 HTTP/1.1" 200 11400 -64.242.88.10 - - [07/Mar/2004:17:13:50 -0800] "GET /twiki/bin/edit/TWiki/DefaultPlugin?t=1078688936 HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:17:16:00 -0800] "GET /twiki/bin/search/Main/?scope=topic®ex=on&search=^g HTTP/1.1" 200 3675 -64.242.88.10 - - [07/Mar/2004:17:17:27 -0800] "GET /twiki/bin/search/TWiki/?scope=topic®ex=on&search=^d HTTP/1.1" 200 5773 -lj1036.inktomisearch.com - - [07/Mar/2004:17:18:36 -0800] "GET /robots.txt HTTP/1.0" 200 68 -lj1090.inktomisearch.com - - [07/Mar/2004:17:18:41 -0800] "GET /twiki/bin/view/Main/LondonOffice HTTP/1.0" 200 3860 -64.242.88.10 - - [07/Mar/2004:17:21:44 -0800] "GET /twiki/bin/attach/TWiki/TablePlugin HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:17:22:49 -0800] "GET /twiki/bin/view/TWiki/ManagingWebs?rev=1.22 HTTP/1.1" 200 9310 -64.242.88.10 - - [07/Mar/2004:17:23:54 -0800] "GET /twiki/bin/statistics/Main HTTP/1.1" 200 808 -64.242.88.10 - - [07/Mar/2004:17:26:30 -0800] "GET /twiki/bin/view/TWiki/WikiCulture HTTP/1.1" 200 5935 -64.242.88.10 - - [07/Mar/2004:17:27:37 -0800] "GET /twiki/bin/edit/Main/WebSearch?t=1078669682 HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:17:28:45 -0800] "GET /twiki/bin/oops/TWiki/ResetPassword?template=oopsmore¶m1=1.4¶m2=1.4 HTTP/1.1" 200 11281 -64.242.88.10 - - [07/Mar/2004:17:29:59 -0800] "GET /twiki/bin/view/TWiki/ManagingWebs?skin=print HTTP/1.1" 200 8806 -64.242.88.10 - - [07/Mar/2004:17:31:39 -0800] "GET /twiki/bin/edit/Main/UvscanAndPostFix?topicparent=Main.WebHome HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:17:35:35 -0800] "GET /twiki/bin/view/TWiki/KlausWriessnegger HTTP/1.1" 200 3848 -64.242.88.10 - - [07/Mar/2004:17:39:39 -0800] "GET /twiki/bin/view/Main/SpamAssassin HTTP/1.1" 200 4081 -64.242.88.10 - - [07/Mar/2004:17:42:15 -0800] "GET /twiki/bin/oops/TWiki/RichardDonkin?template=oopsmore¶m1=1.2¶m2=1.2 HTTP/1.1" 200 11281 -64.242.88.10 - - [07/Mar/2004:17:46:17 -0800] "GET /twiki/bin/rdiff/TWiki/AlWilliams?rev1=1.3&rev2=1.2 HTTP/1.1" 200 4485 -64.242.88.10 - - [07/Mar/2004:17:47:43 -0800] "GET /twiki/bin/rdiff/TWiki/AlWilliams?rev1=1.2&rev2=1.1 HTTP/1.1" 200 5234 -64.242.88.10 - - [07/Mar/2004:17:50:44 -0800] "GET /twiki/bin/view/TWiki/SvenDowideit HTTP/1.1" 200 3616 -64.242.88.10 - - [07/Mar/2004:17:53:45 -0800] "GET /twiki/bin/search/Main/SearchResult?scope=text®ex=on&search=Office%20*Locations[^A-Za-z] HTTP/1.1" 200 7771 diff --git a/src/Unit-1/CSharp/lesson4/Images/Thumbs.db b/src/Unit-1/CSharp/lesson4/Images/Thumbs.db deleted file mode 100644 index 476553abb..000000000 Binary files a/src/Unit-1/CSharp/lesson4/Images/Thumbs.db and /dev/null differ diff --git a/src/Unit-1/CSharp/lesson4/README.md b/src/Unit-1/CSharp/lesson4/README.md deleted file mode 100644 index d360e6982..000000000 --- a/src/Unit-1/CSharp/lesson4/README.md +++ /dev/null @@ -1,745 +0,0 @@ -# Lesson 1.4: Child Actors, Actor Hierarchies, and Supervision -This lesson will make a big jump forward in both the capabilities of our codebase, and in your understanding of how the actor model works. - -This lesson is our most challenging one yet, so let's get right to it! - -## Key concepts / background -Before we get into the details of the actor hierarchy itself, let's stop and ask: why do we need a hierarchy at all? - -There are two key reasons actors exist in a hierarchy: - -1. To atomize work and turn massive amounts of data into manageable chunks -1. To contain errors and make the system resilient - -### Hierarchies atomize work -Having a hierarchy helps our system to break down work into smaller and smaller pieces, and to allow for different skill specializations at different levels of the hierarchy. - -A common way this is realized in an actor systems is that large data streams get atomized, broken down over and over again until they are small and can easily be dealt with by a small code footprint. - -Let's take Twitter as an example (users of JVM Akka). Using Akka, Twitter is able to break up their massive ingestion of data into small, manageable streams of information that they can react to. For instance - Twitter can break up their giant firehose of tweets into individual streams for the timeline of each user currently on the site, and they can use Akka to push messages that have arrived for that user into their stream via websocket / etc. - -What's the pattern? Take a lot of work. Break it down recursively until it is easily dealt with. Respond as needed. - -### Hierarchies enable resilient systems -A hierarchy allows for different levels of risk and specialization to exist that could not otherwise. - -Think of how an army works. An army has a general setting strategy and overseeing everything, but she is usually not going to be on the front line of the battle where there is the most risk. However, she has wide leverage and guides everything. At the same time, there are lower-ranking soldiers who are on the front lines, doing risky operations and carrying out the orders that they receive. - -This is exactly how an actor system operates. - -Higher-level actors are more supervisional in nature, and this allows the actor system to push risk down and to the edges. By pushing risky operations to the edges of the hierarchy, the system can isolate risk and recover from errors without the whole system crashing. - -Both of these concepts are important, but for the rest of this lesson we'll put our emphasis on how actor systems use hierarchies to be resilient. - -How is this achieved? **Supervision.** - -### What is supervision? Why should I care? -Supervision is the basic concept that allows your actor system to quickly isolate and recover from failures. - -Every actor has another actor that supervises it, and helps it recover when errors occur. This is true from the top all the way to the bottom of the hierarchy. - -This supervision ensures that when part of your application encounters an unexpected failure (unhandled exception, network timeout, etc.), that failure will be contained to only the affected part of your actor hierarchy. - -All other actors will keep on working as though nothing happened. We call this "failure isolation" or "containment." - -How is this accomplished? Let's find out… - -### Actor Hierarchies -First, a key point: Every actor has a parent, and some actors have children. Parents supervise their children. - -Since parents supervise their children, this means that ***every actor has a supervisor, and every actor can also BE a supervisor.*** - -Within your actor system, actors are arranged into a hierarchy. This means there are "top level" actors, which essentially report directly to the `ActorSystem` itself, and there are "child" actors, which report to other actors. - -The overall hierarchy looks like this (we'll go through piece by piece in a moment): -![Petabridge Akka.NET Bootcamp Lesson 1.3 Actor Hierarchies](Images/hierarchy_overview.png) - - -### What are the levels of the hierarchy? -#### The base of it all: The "Guardians" -The "guardians" are the root actors of the entire system. - -I'm referring to these three actors at the very top of the hierarchy: -![Petabridge Akka.NET Bootcamp Lesson 1.3 Actor Hierarchies](Images/guardians.png) - -##### The `/` actor - -The `/` actor is the base actor of the entire actor system, and may also be referred to as "The Root Guardian." This actor supervises the `/system` and `/user` actors (the other "Guardians"). - -All actors require another actor as their parent, except this one. This actor is also sometimes called the "bubble-walker" since it is "out of the bubble" of the normal actor system. For now, don't worry about this actor. - -##### The `/system` actor - -The `/system` actor may also be referred to as "The System Guardian". The main job of this actor is to ensure that the system shuts down in an orderly manner, and to maintain/supervise other system actors which implement framework level features and utilities (logging, etc). We'll discuss the system guardian and the system actor hierarchy in a future post. - -##### The `/user` actor - -This is where the party starts! And this is where you'll be spending all your time as a developer. - -The `/user` actor may also be referred to as "The Guardian Actor". But from a user perspective, `/user` is the root of your actor system and is usually just called the "root actor." - -> Generally, "root actor" refers to the `/user` actor. - -As a user, you don't really need to worry too much about the Guardians. We just have to make sure that we use supervision properly under `/user` so that no exception can bubble up to the Guardians and crash the whole system. - - -#### The `/user` actor hierarchy -This is the meat and potatoes of the actor hierarchy: all of the actors you define in your applications. -![Akka: User actor hierarchy](Images/user_actors.png) - -> The direct children of the `/user` actor are called "top level actors." - -Actors are always created as a child of some other actor. - -Whenever you make an actor directly from the context of the actor system itself, that new actor is a top level actor, like so: - -```csharp -// create the top level actors from above diagram -ActorRef a1 = MyActorSystem.ActorOf(Props.Create(), "a1"); -ActorRef a2 = MyActorSystem.ActorOf(Props.Create(), "a2"); -``` - -Now, let's make child actors for `a2` by creating them inside the context of `a2`, our parent-to-be: - -```csharp -// create the children of actor a2 -// this is inside actor a2 -ActorRef b1 = Context.ActorOf(Props.Create(), "b1"); -ActorRef b2 = Context.ActorOf(Props.Create(), "b2"); -``` - -#### Actor path == actor position in hierarchy -Every actor has an address. To send a message from one actor to another, you just have to know it's address (AKA its "ActorPath"). This is what a full actor address looks like: - -![Akka.NET actor address and path](Images/actor_path.png) - -> *The "Path" portion of an actor address is just a description of where that actor is in your actor hierarchy. Each level of the hierarchy is separated by a single slash ('/').* - -For example, if we were running on `localhost`, the full address of actor `b2` would be `akka.tcp://MyActorSystem@localhost:9001/user/a1/b2`. - -One question that comes up a lot is, "Do my actor classes have to live at a certain point in the hierarchy?" For example, if I have an actor class, `FooActor`—can I only deploy that actor as a child of `BarActor` on the hierarchy? Or can I deploy it anywhere? - -The answer is **any actor may be placed anywhere in your actor hierarchy**. - -> *Any actor may be placed anywhere in your actor hierarchy.* - -Okay, now that we've got this hierarchy business down, let's do something interesting with it. Like supervising! - -### How supervision works in the actor hierarchy -Now that you know how actors are organized, know this: actors supervise their children. *But, they only supervise the level that is immediately below them in the hierarchy (actors do not supervise their grandchildren, great-grandchildren, etc).* - -> Actors only supervise their children, the level immediately below them in the hierarchy. - -#### When does supervision come into play? Errors! -When things go wrong, that's when! Whenever a child actor has an unhandled exception and is crashing, it reaches out to its parent for help and to tell it what to do. - -Specifically, the child will send its parent a message that is of the `Failure` class. Then it's up to the parent to decide what to do. - -#### How can the parent resolve the error? -There are two factors that determine how a failure is resolved: - -1. How the child failed (what type of `Exception` did the child include in its `Failure` message to its parent.) -1. What Directive the parent actor executes in response to a child `Failure`. This is determined by the parent's `SupervisionStrategy`. - -##### Here's the sequence of events when an error occurs: - -1. Unhandled exception occurs in child actor (`c1`), which is supervised by its parent (`p1`). -2. `c1` suspends operations. -3. The system sends a `Failure` message from `c1` to `p1`, with the `Exception` that was raised. -4. `p1` issues a directive to `c1` telling it what to do. -5. Life goes on, and the affected part of the system heals itself without burning down the whole house. Kittens and unicorns, handing out free ice cream and coffee to be enjoyed while relaxing on a pillowy rainbow. Yay! - - -##### Supervision directives -When it receives an error from its child, a parent can take one of the following actions ("directives"). The supervision strategy maps different exception types to these directives, allowing you to handle different types of errors as appropriate. - -Types of supervision directives (i.e. what decisions a supervisor can make): - -- **Restart** the child (default): this is the common case, and the default. -- **Stop** the child: this permanently terminates the child actor. -- **Escalate** the error (and stop itself): this is the parent saying "I don't know what to do! I'm gonna stop everything and ask MY parent!" -- **Resume** processing (ignores the error): you generally won't use this. Ignore it for now. - -> *The critical thing to know here is that ***whatever action is taken on a parent propagates to its children***. If a parent is halted, all its children halt. If it is restarted, all its children restart.* - -##### Supervision strategies -There are two built-in supervision strategies: - -1. One-For-One Strategy (default) -2. All-For-One Strategy - - The basic difference between these is how widespread the effects of the error-resolution directive will be. - -**One-For-One** says that that the directive issued by the parent only applies to the failing child actor. It has no effect on the siblings of the failing child. This is the default strategy if you don't specify one. (You can also define your own custom supervision strategy.) - -**All-For-One** says that that the directive issued by the parent applies to the failing child actor AND all of its siblings. - -The other important choice you make in a supervision strategy is how many times a child can fail within a given period of time before it is shut down (e.g. "no more than 10 errors within 60 seconds, or you're shut down"). - -Here's an example supervision strategy: - -```csharp -public class MyActor : UntypedActor -{ - // if any child of MyActor throws an exception, apply the rules below - // e.g. Restart the child, if 10 exceptions occur in 30 seconds or - // less, then stop the actor - protected override SupervisorStrategy SupervisorStrategy() - { - return new OneForOneStrategy(// or AllForOneStrategy - maxNumberOfRetries: 10, - duration: TimeSpan.FromSeconds(30), - decider: x => - { - // Maybe ArithmeticException is not application critical - // so we just ignore the error and keep going. - if (x is ArithmeticException) return Directive.Resume; - - // Error that we have no idea what to do with - else if (x is InsanelyBadException) return Directive.Escalate; - - // Error that we can't recover from, stop the failing child - else if (x is NotSupportedException) return Directive.Stop; - - // otherwise restart the failing child - else return Directive.Restart; - }); - } - - ... -} -``` - -### What's the point? Containment. -The whole point of supervision strategies and directives is to contain failure within the system and self-heal, so the whole system doesn't crash. How do we do this? - -We push potentially-dangerous operations from a parent to a child, whose only job is to carry out the dangerous task. - -For example, let's say we're running a stats system during the World Cup, that keeps scores and player statistics from a bunch of games in the World Cup. - -Now, being the World Cup, there could be huge demand on that API and it could get throttled, start rate-limiting, or just plain crash (no offense FIFA, I love you guys and the Cup). We'll use the epic Germany-Ghana match as an example. - -But our scorekeeper has to periodically update its data as the game progresses. Let's assume it has to call to an external API maintained by FIFA to get the data it needs. - -***This network call is dangerous!*** If the request raises an error, it will crash the actor that started the call. So how do we protect ourselves? - -We keep the stats in a parent actor, and push that nasty network call down into a child actor. That way, if the child crashes, it doesn't affect the parent, which is holding on to all the important data. By doing this, we are **localizing the failure** and keeping it from spreading throughout the system. - -Here's an example of how we could structure the actor hierarchy to safely accomplish the goal: - -![Akka: User actor hierarchy](Images/error_kernel.png) - -Recall that we could have many clones of this exact structure working in parallel, with one clone per game we are tracking. **And we wouldn't have to write any new code to scale it out!** Beautiful. - -> You may also hear people use the term "error kernel," which refers to how much of the system is affected by the failure. You may also hear "error kernel pattern," which is just fancy shorthand for the approach I just explained where we push dangerous behavior to child actors to isolate/protect the parent. - -## Exercise -To start off, we need to do some upgrading of our system. We are going to add in the components which will enable our actor system to actually monitor a file for changes. We have most of the classes we need, but there are a few pieces of utility code that we need to add. - -We're almost done! We really just need to add the `TailCoordinatorActor`, `TailActor`, and the `FileObserver`. - -The goal of this exercise is to show you how to make a parent/child actor relationship. - -### Phase 1: A quick bit of prep -#### Replace `ValidationActor` with `FileValidatorActor` -Since we're shifting to actually looking at files now, go ahead and replace `ValidationActor` with `FileValidatorActor`. - -Add a new class, `FileValidatorActor`, with [this code](Completed/FileValidatorActor.cs): - -```csharp -// FileValidatorActor.cs -using System.IO; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor that validates user input and signals result to others. - /// - public class FileValidatorActor : UntypedActor - { - private readonly ActorRef _consoleWriterActor; - private readonly ActorRef _tailCoordinatorActor; - - public FileValidatorActor(ActorRef consoleWriterActor, ActorRef tailCoordinatorActor) - { - _consoleWriterActor = consoleWriterActor; - _tailCoordinatorActor = tailCoordinatorActor; - } - - protected override void OnReceive(object message) - { - var msg = message as string; - if (string.IsNullOrEmpty(msg)) - { - // signal that the user needs to supply an input - _consoleWriterActor.Tell(new Messages.NullInputError("Input was blank. Please try again.\n")); - - // tell sender to continue doing its thing (whatever that may be, this actor doesn't care) - Sender.Tell(new Messages.ContinueProcessing()); - } - else - { - var valid = IsFileUri(msg); - if (valid) - { - // signal successful input - _consoleWriterActor.Tell(new Messages.InputSuccess(string.Format("Starting processing for {0}", msg))); - - // start coordinator - _tailCoordinatorActor.Tell(new TailCoordinatorActor.StartTail(msg, _consoleWriterActor)); - } - else - { - // signal that input was bad - _consoleWriterActor.Tell(new Messages.ValidationError(string.Format("{0} is not an existing URI on disk.", msg))); - - // tell sender to continue doing its thing (whatever that may be, this actor doesn't care) - Sender.Tell(new Messages.ContinueProcessing()); - } - } - - - - } - - /// - /// Checks if file exists at path provided by user. - /// - /// - /// - private static bool IsFileUri(string path) - { - return File.Exists(path); - } - } -} -``` - -You'll also want to make sure to update the `Props` instance in `Main` that references the class: - -```csharp -// Program.cs -Props validationActorProps = Props.Create(() => new FileValidatorActor(consoleWriterActor)); -``` - -#### Update `DoPrintInstructions` -Just making a slight tweak to our instructions here, since we'll be using a text file on disk going forward instead of prompting the user for input. - -Update `DoPrintInstructions()` to this: - -```csharp -// ConsoleReaderActor.cs -private void DoPrintInstructions() -{ - Console.WriteLine("Please provide the URI of a log file on disk.\n"); -} -``` - -#### Add `FileObserver` -This is a utility class that we're providing for you to use. It does the low-level work of actually watching a file for changes. - -Create a new class called `FileObserver` and type in thi code for [FileObserver.cs](Completed/FileObserver.cs). If you're running this on Mono, note the extra environment variable that has to be uncommented in the `Start()` method: - -```csharp -// FileObserver.cs -using System; -using System.IO; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Turns events about a specific file into messages for . - /// - public class FileObserver : IDisposable - { - private readonly ActorRef _tailActor; - private readonly string _absoluteFilePath; - private FileSystemWatcher _watcher; - private readonly string _fileDir; - private readonly string _fileNameOnly; - - public FileObserver(ActorRef tailActor, string absoluteFilePath) - { - _tailActor = tailActor; - _absoluteFilePath = absoluteFilePath; - _fileDir = Path.GetDirectoryName(absoluteFilePath); - _fileNameOnly = Path.GetFileName(absoluteFilePath); - } - - /// - /// Begin monitoring file. - /// - public void Start() - { - // Need this for Mono 3.12.0 workaround - // Environment.SetEnvironmentVariable("MONO_MANAGED_WATCHER", "enabled"); // uncomment this line if you're running on Mono! - - // make watcher to observe our specific file - _watcher = new FileSystemWatcher(_fileDir, _fileNameOnly); - - // watch our file for changes to the file name, or new messages being written to file - _watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite; - - // assign callbacks for event types - _watcher.Changed += OnFileChanged; - _watcher.Error += OnFileError; - - // start watching - _watcher.EnableRaisingEvents = true; - - } - - /// - /// Stop monitoring file. - /// - public void Dispose() - { - _watcher.Dispose(); - } - - /// - /// Callback for file error events. - /// - /// - /// - void OnFileError(object sender, ErrorEventArgs e) - { - _tailActor.Tell(new TailActor.FileError(_fileNameOnly, e.GetException().Message), ActorRef.NoSender); - } - - /// - /// Callback for file change events. - /// - /// - /// - void OnFileChanged(object sender, FileSystemEventArgs e) - { - if (e.ChangeType == WatcherChangeTypes.Changed) - { - // here we use a special ActorRef.NoSender - // since this event can happen many times, this is a little microoptimization - _tailActor.Tell(new TailActor.FileWrite(e.Name), ActorRef.NoSender); - } - - } - - } -} -``` - -// just adding `tailCoordinatorActor` arg to this Props -### Phase 2: Make your first parent/child actors! -Great! Now we're ready to create our actor classes that will form a parent/child relationship. - -Recall that in the hierarchy we're going for, there is a `TailCoordinatorActor` that coordinates child actors to actually monitor and tail files. For now it will only supervise one child, `TailActor`, but in the future it can easily expand to have many children, each observing/tailing a different file. - -#### Add `TailCoordinatorActor` -Create a new class called `TailCoordinatorActor` in a file of the same name. - -Add the following code, which defines our coordinator actor (which will soon be our first parent actor). - -```csharp -// TailCoordinatorActor.cs -using System; -using Akka.Actor; - -namespace WinTail -{ - public class TailCoordinatorActor : UntypedActor - { - #region Message types - /// - /// Start tailing the file at user-specified path. - /// - public class StartTail - { - public StartTail(string filePath, ActorRef reporterActor) - { - FilePath = filePath; - ReporterActor = reporterActor; - } - - public string FilePath { get; private set; } - - public ActorRef ReporterActor { get; private set; } - } - - /// - /// Stop tailing the file at user-specified path. - /// - public class StopTail - { - public StopTail(string filePath) - { - FilePath = filePath; - } - - public string FilePath { get; private set; } - } - - #endregion - - protected override void OnReceive(object message) - { - if (message is StartTail) - { - var msg = message as StartTail; - // YOU NEED TO FILL IN HERE - } - - } - } -} - - - -``` - -#### Create `ActorRef` for `TailCoordinatorActor` -In `Main()`, create a new `ActorRef` for `TailCoordinatorActor` and then pass it into `fileValidatorActorProps`, like so: - -```csharp -// Program.Main -// make tailCoordinatorActor -Props tailCoordinatorProps = Props.Create(() => new TailCoordinatorActor()); -ActorRef tailCoordinatorActor = MyActorSystem.ActorOf(tailCoordinatorProps, "tailCoordinatorActor"); - -// pass tailCoordinatorActor to fileValidatorActorProps (just adding one extra arg) -Props fileValidatorActorProps = Props.Create(() => new FileValidatorActor(consoleWriterActor, tailCoordinatorActor)); -ActorRef validationActor = MyActorSystem.ActorOf(fileValidatorActorProps, "validationActor"); -``` - -#### Add `TailActor` -Now, add a class called `TailActor` in its own file. This actor is the actor that is actually responsible for tailing a given file. `TailActor` will be created and supervised by `TailCoordinatorActor` in a moment. - -For now, add the following code in `TailActor.cs`: - -```csharp -// TailActor.cs -using System.IO; -using System.Text; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Monitors the file at for changes and sends file updates to console. - /// - public class TailActor : UntypedActor - { - #region Message types - - /// - /// Signal that the file has changed, and we need to read the next line of the file. - /// - public class FileWrite - { - public FileWrite(string fileName) - { - FileName = fileName; - } - - public string FileName { get; private set; } - } - - /// - /// Signal that the OS had an error accessing the file. - /// - public class FileError - { - public FileError(string fileName, string reason) - { - FileName = fileName; - Reason = reason; - } - - public string FileName { get; private set; } - - public string Reason { get; private set; } - } - - /// - /// Signal to read the initial contents of the file at actor startup. - /// - public class InitialRead - { - public InitialRead(string fileName, string text) - { - FileName = fileName; - Text = text; - } - - public string FileName { get; private set; } - public string Text { get; private set; } - } - - #endregion - - private readonly string _filePath; - private readonly ActorRef _reporterActor; - private readonly FileObserver _observer; - private readonly Stream _fileStream; - private readonly StreamReader _fileStreamReader; - - public TailActor(ActorRef reporterActor, string filePath) - { - _reporterActor = reporterActor; - _filePath = filePath; - - // start watching file for changes - _observer = new FileObserver(Self, Path.GetFullPath(_filePath)); - _observer.Start(); - - // open the file stream with shared read/write permissions (so file can be written to while open) - _fileStream = new FileStream(Path.GetFullPath(_filePath), FileMode.Open, FileAccess.Read, - FileShare.ReadWrite); - _fileStreamReader = new StreamReader(_fileStream, Encoding.UTF8); - - // read the initial contents of the file and send it to console as first message - var text = _fileStreamReader.ReadToEnd(); - Self.Tell(new InitialRead(_filePath, text)); - } - - protected override void OnReceive(object message) - { - if (message is FileWrite) - { - // move file cursor forward - // pull results from cursor to end of file and write to output - // (tis is assuming a log file type format that is append-only) - var text = _fileStreamReader.ReadToEnd(); - if (!string.IsNullOrEmpty(text)) - { - _reporterActor.Tell(text); - } - - } - else if (message is FileError) - { - var fe = message as FileError; - _reporterActor.Tell(string.Format("Tail error: {0}", fe.Reason)); - } - else if (message is InitialRead) - { - var ir = message as InitialRead; - _reporterActor.Tell(ir.Text); - } - } - } -} -``` - -#### Add `TailActor` as a child of `TailCoordinatorActor` -Quick review: `TailActor` is to be a child of `TailCoordinatorActor` and will therefore be supervised by `TailCoordinatorActor`. - -This also means that `TailActor` must be created in the context of `TailCoordinatorActor`. - -Go to `TailCoordinatorActor.cs` and replace `OnReceive()` with the following code to create your first child actor! - -```csharp -// TailCoordinatorActor.OnReceive -protected override void OnReceive(object message) -{ - if (message is StartTail) - { - var msg = message as StartTail; - // here we are creating our first parent/child relationship! - // the TailActor instance created here is a child - // of this instance of TailCoordinatorActor - Context.ActorOf(Props.Create(() => new TailActor(msg.ReporterActor, msg.FilePath))); - } - -} -``` - -### ***BAM!*** -You have just established your first parent/child actor relationship! - -### Phase 3: Implement a `SupervisorStrategy` -Now it's time to add a supervision strategy to your new parent, `TailCoordinatorActor`. - -The default `SupervisorStrategy` is a One-For-One strategy ([docs](http://getakka.net/wiki/Supervision#one-for-one-strategy-vs-all-for-one-strategy)) w/ a Restart directive ([docs](http://getakka.net/wiki/Supervision#what-restarting-means)). - -Add this code to the bottom of `TailCoordinatorActor`: - -```csharp -// TailCoordinatorActor.cs -protected override SupervisorStrategy SupervisorStrategy() -{ - return new OneForOneStrategy ( - 10, // maxNumberOfRetries - TimeSpan.FromSeconds(30), // duration - decider: x => - { - //Maybe we consider ArithmeticException to not be application critical - //so we just ignore the error and keep going. - if (x is ArithmeticException) return Directive.Resume; - - //Error that we cannot recover from, stop the failing actor - else if (x is NotSupportedException) return Directive.Stop; - - //In all other cases, just restart the failing actor - else return Directive.Restart; - }); -} -``` - -### Phase 4: Build and Run! -Awesome! It's time to fire this baby up and see it in action. - -#### Get a text file you can tail -We recommend a log file like [this sample one](DoThis/sample_log_file.txt), but you can also just make a plain text file and fill it with whatever you want. - -Open the text file up and put it on one side of your screen. - -#### Fire it up -##### Check the starting output -Run the application and you should see a console window open up and print out the starting contents of your log file. The starting state should look like this if you're using the sample log file we provided: -![Petabridge Akka.NET Bootcamp Actor Hierarchies](Images/working_tail_1.png) - -**Leave both the console and the file open, and then...** - -##### Add text and see if the `tail` works! -Add some lines of text to the text file, save it, and watch it show up in the `tail`! - -It should look something like this: -![Petabridge Akka.NET Bootcamp Actor Hierarchies](Images/working_tail_2.png) - -Congrats! YOU HAVE JUST MADE A PORT OF `tail` IN .NET! - -### Once you're done -Compare your code to the solution in the [Completed](Completed/) folder to see what the instructors included in their samples. - -## Great job! Onto Lesson 5! -Awesome work! Well done on completing this lesson, we know it was a bear! It was a big jump forward for our system and in your understanding. - -Here is a high-level overview of our working system! - -![Akka.NET Unit 1 Tail System Diagram](Images/system_overview.png) - -**Let's move onto [Lesson 5 - Looking up Actors by Address with `ActorSelection`](../lesson5).** - ---- -## Supervision FAQ -### How long do child actors have to wait for their supervisor? -This is a common question we get: What if there are a bunch of messages already in the supervisor's mailbox waiting to be processed when a child reports an error? Won't the crashing child actor have to wait until those are processed until it gets a response? - -Actually, no. When an actor reports an error to its supervisor, it is sent as a special type of "system message." *System messages jump to the front of the supervisor's mailbox and are processed before the supervisor returns to its normal processing.* - -> *System messages jump to the front of the supervisor's mailbox and are processed before the supervisor returns to its normal processing.* - -Parents come with a default SupervisorStrategy object (or you can provide a custom one) that makes decisions on how to handle failures with their child actors. - -### But what happens to the current message when an actor fails? -The current message being processed by an actor when it is halted (regardless of whether the failure happened to it or its parent) can be saved and re-processed after restarting. There are several ways to do this. The most common approach used is during `preRestart()`, the actor can stash the message (if it has a stash) or it can send the message to another actor that will send it back once restarted. (Note: If the actor has a stash, it will automatically unstash the message once it successfully restarts.) - - -## Any questions? -**Don't be afraid to ask questions** :). - -Come ask any questions you have, big or small, [in this ongoing Bootcamp chat with the Petabridge & Akka.NET teams](https://gitter.im/petabridge/akka-bootcamp). - -### Problems with the code? -If there is a problem with the code running, or something else that needs to be fixed in this lesson, please [create an issue](/issues) and we'll get right on it. This will benefit everyone going through Bootcamp. diff --git a/src/Unit-1/CSharp/lesson5/Completed/App.config b/src/Unit-1/CSharp/lesson5/Completed/App.config deleted file mode 100644 index 8e1564635..000000000 --- a/src/Unit-1/CSharp/lesson5/Completed/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson5/Completed/ConsoleReaderActor.cs b/src/Unit-1/CSharp/lesson5/Completed/ConsoleReaderActor.cs deleted file mode 100644 index ac2e4e101..000000000 --- a/src/Unit-1/CSharp/lesson5/Completed/ConsoleReaderActor.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor responsible for reading FROM the console. - /// Also responsible for calling . - /// - class ConsoleReaderActor : UntypedActor - { - public const string StartCommand = "start"; - public const string ExitCommand = "exit"; - - protected override void OnReceive(object message) - { - if (message.Equals(StartCommand)) - { - DoPrintInstructions(); - } - - GetAndValidateInput(); - } - - - #region Internal methods - private void DoPrintInstructions() - { - Console.WriteLine("Please provide the URI of a log file on disk.\n"); - } - - - /// - /// Reads input from console, validates it, then signals appropriate response - /// (continue processing, error, success, etc.). - /// - private void GetAndValidateInput() - { - var message = Console.ReadLine(); - if (!string.IsNullOrEmpty(message) && String.Equals(message, ExitCommand, StringComparison.OrdinalIgnoreCase)) - { - // if user typed ExitCommand, shut down the entire actor system (allows the process to exit) - Context.System.Shutdown(); - return; - } - - // otherwise, just send the message off for validation - Context.ActorSelection("/user/validationActor").Tell(message); - } - #endregion - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson5/Completed/ConsoleWriterActor.cs b/src/Unit-1/CSharp/lesson5/Completed/ConsoleWriterActor.cs deleted file mode 100644 index 05efe1a08..000000000 --- a/src/Unit-1/CSharp/lesson5/Completed/ConsoleWriterActor.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor responsible for serializing message writes to the console. - /// (write one message at a time, champ :) - /// - class ConsoleWriterActor : UntypedActor - { - protected override void OnReceive(object message) - { - if (message is Messages.InputError) - { - var msg = message as Messages.InputError; - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine(msg.Reason); - } - else if (message is Messages.InputSuccess) - { - var msg = message as Messages.InputSuccess; - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine(msg.Reason); - } - else - { - Console.WriteLine(message); - } - - Console.ResetColor(); - } - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson5/Completed/FileObserver.cs b/src/Unit-1/CSharp/lesson5/Completed/FileObserver.cs deleted file mode 100644 index a675e8fbf..000000000 --- a/src/Unit-1/CSharp/lesson5/Completed/FileObserver.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.IO; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Turns events about a specific file into messages for . - /// - public class FileObserver : IDisposable - { - private readonly ActorRef _tailActor; - private readonly string _absoluteFilePath; - private FileSystemWatcher _watcher; - private readonly string _fileDir; - private readonly string _fileNameOnly; - - public FileObserver(ActorRef tailActor, string absoluteFilePath) - { - _tailActor = tailActor; - _absoluteFilePath = absoluteFilePath; - _fileDir = Path.GetDirectoryName(absoluteFilePath); - _fileNameOnly = Path.GetFileName(absoluteFilePath); - } - - /// - /// Begin monitoring file. - /// - public void Start() - { - // make watcher to observe our specific file - _watcher = new FileSystemWatcher(_fileDir, _fileNameOnly); - - // watch our file for changes to the file name, or new messages being written to file - _watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite; - - // assign callbacks for event types - _watcher.Changed += OnFileChanged; - _watcher.Error += OnFileError; - - // start watching - _watcher.EnableRaisingEvents = true; - - } - - /// - /// Stop monitoring file. - /// - public void Dispose() - { - _watcher.Dispose(); - } - - /// - /// Callback for file error events. - /// - /// - /// - void OnFileError(object sender, ErrorEventArgs e) - { - _tailActor.Tell(new TailActor.FileError(_fileNameOnly, e.GetException().Message), ActorRef.NoSender); - } - - /// - /// Callback for file change events. - /// - /// - /// - void OnFileChanged(object sender, FileSystemEventArgs e) - { - if (e.ChangeType == WatcherChangeTypes.Changed) - { - // here we use a special ActorRef.NoSender - // since this event can happen many times, this is a little microoptimization - _tailActor.Tell(new TailActor.FileWrite(e.Name), ActorRef.NoSender); - } - - } - - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson5/Completed/FileValidatorActor.cs b/src/Unit-1/CSharp/lesson5/Completed/FileValidatorActor.cs deleted file mode 100644 index 0b60cb27a..000000000 --- a/src/Unit-1/CSharp/lesson5/Completed/FileValidatorActor.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.IO; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor that validates user input and signals result to others. - /// - public class FileValidatorActor : UntypedActor - { - private readonly ActorRef _consoleWriterActor; - - public FileValidatorActor(ActorRef consoleWriterActor) - { - _consoleWriterActor = consoleWriterActor; - } - - protected override void OnReceive(object message) - { - var msg = message as string; - if (string.IsNullOrEmpty(msg)) - { - // signal that the user needs to supply an input - _consoleWriterActor.Tell(new Messages.NullInputError("Input was blank. Please try again.\n")); - - // tell sender to continue doing its thing (whatever that may be, this actor doesn't care) - Sender.Tell(new Messages.ContinueProcessing()); - } - else - { - var valid = IsFileUri(msg); - if (valid) - { - // signal successful input - _consoleWriterActor.Tell(new Messages.InputSuccess(string.Format("Starting processing for {0}", msg))); - - // start coordinator - Context.ActorSelection("/user/tailCoordinatorActor").Tell(new TailCoordinatorActor.StartTail(msg, _consoleWriterActor)); - } - else - { - // signal that input was bad - _consoleWriterActor.Tell(new Messages.ValidationError(string.Format("{0} is not an existing URI on disk.", msg))); - - // tell sender to continue doing its thing (whatever that may be, this actor doesn't care) - Sender.Tell(new Messages.ContinueProcessing()); - } - } - - - - } - - /// - /// Checks if file exists at path provided by user. - /// - /// - /// - private static bool IsFileUri(string path) - { - return File.Exists(path); - } - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson5/Completed/Messages.cs b/src/Unit-1/CSharp/lesson5/Completed/Messages.cs deleted file mode 100644 index f322a76ed..000000000 --- a/src/Unit-1/CSharp/lesson5/Completed/Messages.cs +++ /dev/null @@ -1,59 +0,0 @@ -namespace WinTail -{ - class Messages - { - - #region Neutral/system messages - /// - /// Marker class to continue processing. - /// - public class ContinueProcessing { } - #endregion - - #region Success messages - /// - /// Base class for signalling that user input was valid. - /// - public class InputSuccess - { - public InputSuccess(string reason) - { - Reason = reason; - } - - public string Reason { get; private set; } - } - #endregion - - #region Error messages - /// - /// Base class for signalling that user input was invalid. - /// - public class InputError - { - public InputError(string reason) - { - Reason = reason; - } - - public string Reason { get; private set; } - } - - /// - /// User provided blank input. - /// - public class NullInputError : InputError - { - public NullInputError(string reason) : base(reason) { } - } - - /// - /// User provided invalid input (currently, input w/ odd # chars) - /// - public class ValidationError : InputError - { - public ValidationError(string reason) : base(reason) { } - } - #endregion - } -} diff --git a/src/Unit-1/CSharp/lesson5/Completed/Program.cs b/src/Unit-1/CSharp/lesson5/Completed/Program.cs deleted file mode 100644 index da2c049b2..000000000 --- a/src/Unit-1/CSharp/lesson5/Completed/Program.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Akka.Actor; - -namespace WinTail -{ - class Program - { - public static ActorSystem MyActorSystem; - - static void Main(string[] args) - { - // make actor system - MyActorSystem = ActorSystem.Create("MyActorSystem"); - - // create top-level actors within the actor system - Props consoleWriterProps = Props.Create(); - ActorRef consoleWriterActor = MyActorSystem.ActorOf(consoleWriterProps, "consoleWriterActor"); - - Props tailCoordinatorProps = Props.Create(() => new TailCoordinatorActor()); - ActorRef tailCoordinatorActor = MyActorSystem.ActorOf(tailCoordinatorProps, "tailCoordinatorActor"); - - Props fileValidatorActorProps = Props.Create(() => new FileValidatorActor(consoleWriterActor)); - ActorRef fileValidatorActor = MyActorSystem.ActorOf(fileValidatorActorProps, "validationActor"); - - Props consoleReaderProps = Props.Create(); - ActorRef consoleReaderActor = MyActorSystem.ActorOf(consoleReaderProps, "consoleReaderActor"); - - // begin processing - consoleReaderActor.Tell(ConsoleReaderActor.StartCommand); - - // blocks the main thread from exiting until the actor system is shut down - MyActorSystem.AwaitTermination(); - } - - } -} diff --git a/src/Unit-1/CSharp/lesson5/Completed/Properties/AssemblyInfo.cs b/src/Unit-1/CSharp/lesson5/Completed/Properties/AssemblyInfo.cs deleted file mode 100644 index 6caf4a85a..000000000 --- a/src/Unit-1/CSharp/lesson5/Completed/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Lesson1-1")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Lesson1-1")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("60155426-eaa6-430b-bac5-1a0c7dc0cfcb")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Unit-1/CSharp/lesson5/Completed/TailActor.cs b/src/Unit-1/CSharp/lesson5/Completed/TailActor.cs deleted file mode 100644 index 099d717a2..000000000 --- a/src/Unit-1/CSharp/lesson5/Completed/TailActor.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System.IO; -using System.Text; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Monitors the file at for changes and sends file updates to console. - /// - public class TailActor : UntypedActor - { - #region Message types - - /// - /// Signal that the file has changed, and we need to read the next line of the file. - /// - public class FileWrite - { - public FileWrite(string fileName) - { - FileName = fileName; - } - - public string FileName { get; private set; } - } - - /// - /// Signal that the OS had an error accessing the file. - /// - public class FileError - { - public FileError(string fileName, string reason) - { - FileName = fileName; - Reason = reason; - } - - public string FileName { get; private set; } - - public string Reason { get; private set; } - } - - /// - /// Signal to read the initial contents of the file at actor startup. - /// - public class InitialRead - { - public InitialRead(string fileName, string text) - { - FileName = fileName; - Text = text; - } - - public string FileName { get; private set; } - public string Text { get; private set; } - } - - #endregion - - private readonly string _filePath; - private readonly ActorRef _reporterActor; - private readonly FileObserver _observer; - private readonly Stream _fileStream; - private readonly StreamReader _fileStreamReader; - - public TailActor(ActorRef reporterActor, string filePath) - { - _reporterActor = reporterActor; - _filePath = filePath; - - // start watching file for changes - _observer = new FileObserver(Self, Path.GetFullPath(_filePath)); - _observer.Start(); - - // open the file stream with shared read/write permissions (so file can be written to while open) - _fileStream = new FileStream(Path.GetFullPath(_filePath), FileMode.Open, FileAccess.Read, - FileShare.ReadWrite); - _fileStreamReader = new StreamReader(_fileStream, Encoding.UTF8); - - // read the initial contents of the file and send it to console as first message - var text = _fileStreamReader.ReadToEnd(); - Self.Tell(new InitialRead(_filePath, text)); - } - - protected override void OnReceive(object message) - { - if (message is FileWrite) - { - // move file cursor forward - // pull results from cursor to end of file and write to output - // (tis is assuming a log file type format that is append-only) - var text = _fileStreamReader.ReadToEnd(); - if (!string.IsNullOrEmpty(text)) - { - _reporterActor.Tell(text); - } - - } - else if (message is FileError) - { - var fe = message as FileError; - _reporterActor.Tell(string.Format("Tail error: {0}", fe.Reason)); - } - else if (message is InitialRead) - { - var ir = message as InitialRead; - _reporterActor.Tell(ir.Text); - } - } - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson5/Completed/TailCoordinatorActor.cs b/src/Unit-1/CSharp/lesson5/Completed/TailCoordinatorActor.cs deleted file mode 100644 index c1ee2e0c7..000000000 --- a/src/Unit-1/CSharp/lesson5/Completed/TailCoordinatorActor.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using Akka.Actor; - -namespace WinTail -{ - public class TailCoordinatorActor : UntypedActor - { - #region Message types - /// - /// Start tailing the file at user-specified path. - /// - public class StartTail - { - public StartTail(string filePath, ActorRef reporterActor) - { - FilePath = filePath; - ReporterActor = reporterActor; - } - - public string FilePath { get; private set; } - - public ActorRef ReporterActor { get; private set; } - } - - /// - /// Stop tailing the file at user-specified path. - /// - public class StopTail - { - public StopTail(string filePath) - { - FilePath = filePath; - } - - public string FilePath { get; private set; } - } - - #endregion - - protected override void OnReceive(object message) - { - if (message is StartTail) - { - var msg = message as StartTail; - Context.ActorOf(Props.Create(() => new TailActor(msg.ReporterActor, msg.FilePath))); - } - - } - - // here we are overriding the default SupervisorStrategy - // which is a One-For-One strategy w/ a Restart directive - protected override SupervisorStrategy SupervisorStrategy() - { - return new OneForOneStrategy ( - 10, // maxNumberOfRetries - TimeSpan.FromSeconds(30), // duration - decider: x => - { - //Maybe we consider ArithmeticException to not be application critical - //so we just ignore the error and keep going. - if (x is ArithmeticException) return Directive.Resume; - - //Error that we cannot recover from, stop the failing actor - else if (x is NotSupportedException) return Directive.Stop; - - //In all other cases, just restart the failing actor - else return Directive.Restart; - }); - } - } -} - - diff --git a/src/Unit-1/CSharp/lesson5/Completed/WinTail.csproj b/src/Unit-1/CSharp/lesson5/Completed/WinTail.csproj deleted file mode 100644 index 8550da4db..000000000 --- a/src/Unit-1/CSharp/lesson5/Completed/WinTail.csproj +++ /dev/null @@ -1,73 +0,0 @@ - - - - - Debug - AnyCPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F} - Exe - Properties - WinTail - Lesson1-1 - v4.5 - 512 - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - packages\Akka.0.8.0\lib\net45\Akka.dll - - - False - packages\Newtonsoft.Json.6.0.1\lib\net45\Newtonsoft.Json.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson5/Completed/WinTail.sln b/src/Unit-1/CSharp/lesson5/Completed/WinTail.sln deleted file mode 100644 index 1c0539943..000000000 --- a/src/Unit-1/CSharp/lesson5/Completed/WinTail.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinTail", "WinTail.csproj", "{E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/Unit-1/CSharp/lesson5/Completed/packages.config b/src/Unit-1/CSharp/lesson5/Completed/packages.config deleted file mode 100644 index 88dee0f6e..000000000 --- a/src/Unit-1/CSharp/lesson5/Completed/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson5/Completed/sample_log_file.txt b/src/Unit-1/CSharp/lesson5/Completed/sample_log_file.txt deleted file mode 100644 index b6a9efb78..000000000 --- a/src/Unit-1/CSharp/lesson5/Completed/sample_log_file.txt +++ /dev/null @@ -1,50 +0,0 @@ -64.242.88.10 - - [07/Mar/2004:16:05:49 -0800] "GET /twiki/bin/edit/Main/Double_bounce_sender?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:16:06:51 -0800] "GET /twiki/bin/rdiff/TWiki/NewUserTemplate?rev1=1.3&rev2=1.2 HTTP/1.1" 200 4523 -64.242.88.10 - - [07/Mar/2004:16:10:02 -0800] "GET /mailman/listinfo/hsdivision HTTP/1.1" 200 6291 -64.242.88.10 - - [07/Mar/2004:16:11:58 -0800] "GET /twiki/bin/view/TWiki/WikiSyntax HTTP/1.1" 200 7352 -64.242.88.10 - - [07/Mar/2004:16:20:55 -0800] "GET /twiki/bin/view/Main/DCCAndPostFix HTTP/1.1" 200 5253 -64.242.88.10 - - [07/Mar/2004:16:23:12 -0800] "GET /twiki/bin/oops/TWiki/AppendixFileSystem?template=oopsmore¶m1=1.12¶m2=1.12 HTTP/1.1" 200 11382 -64.242.88.10 - - [07/Mar/2004:16:24:16 -0800] "GET /twiki/bin/view/Main/PeterThoeny HTTP/1.1" 200 4924 -64.242.88.10 - - [07/Mar/2004:16:29:16 -0800] "GET /twiki/bin/edit/Main/Header_checks?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12851 -64.242.88.10 - - [07/Mar/2004:16:30:29 -0800] "GET /twiki/bin/attach/Main/OfficeLocations HTTP/1.1" 401 12851 -64.242.88.10 - - [07/Mar/2004:16:31:48 -0800] "GET /twiki/bin/view/TWiki/WebTopicEditTemplate HTTP/1.1" 200 3732 -64.242.88.10 - - [07/Mar/2004:16:32:50 -0800] "GET /twiki/bin/view/Main/WebChanges HTTP/1.1" 200 40520 -64.242.88.10 - - [07/Mar/2004:16:33:53 -0800] "GET /twiki/bin/edit/Main/Smtpd_etrn_restrictions?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12851 -64.242.88.10 - - [07/Mar/2004:16:35:19 -0800] "GET /mailman/listinfo/business HTTP/1.1" 200 6379 -64.242.88.10 - - [07/Mar/2004:16:36:22 -0800] "GET /twiki/bin/rdiff/Main/WebIndex?rev1=1.2&rev2=1.1 HTTP/1.1" 200 46373 -64.242.88.10 - - [07/Mar/2004:16:37:27 -0800] "GET /twiki/bin/view/TWiki/DontNotify HTTP/1.1" 200 4140 -64.242.88.10 - - [07/Mar/2004:16:39:24 -0800] "GET /twiki/bin/view/Main/TokyoOffice HTTP/1.1" 200 3853 -64.242.88.10 - - [07/Mar/2004:16:43:54 -0800] "GET /twiki/bin/view/Main/MikeMannix HTTP/1.1" 200 3686 -64.242.88.10 - - [07/Mar/2004:16:45:56 -0800] "GET /twiki/bin/attach/Main/PostfixCommands HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:16:47:12 -0800] "GET /robots.txt HTTP/1.1" 200 68 -64.242.88.10 - - [07/Mar/2004:16:47:46 -0800] "GET /twiki/bin/rdiff/Know/ReadmeFirst?rev1=1.5&rev2=1.4 HTTP/1.1" 200 5724 -64.242.88.10 - - [07/Mar/2004:16:49:04 -0800] "GET /twiki/bin/view/Main/TWikiGroups?rev=1.2 HTTP/1.1" 200 5162 -64.242.88.10 - - [07/Mar/2004:16:50:54 -0800] "GET /twiki/bin/rdiff/Main/ConfigurationVariables HTTP/1.1" 200 59679 -64.242.88.10 - - [07/Mar/2004:16:52:35 -0800] "GET /twiki/bin/edit/Main/Flush_service_name?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12851 -64.242.88.10 - - [07/Mar/2004:16:53:46 -0800] "GET /twiki/bin/rdiff/TWiki/TWikiRegistration HTTP/1.1" 200 34395 -64.242.88.10 - - [07/Mar/2004:16:54:55 -0800] "GET /twiki/bin/rdiff/Main/NicholasLee HTTP/1.1" 200 7235 -64.242.88.10 - - [07/Mar/2004:16:56:39 -0800] "GET /twiki/bin/view/Sandbox/WebHome?rev=1.6 HTTP/1.1" 200 8545 -64.242.88.10 - - [07/Mar/2004:16:58:54 -0800] "GET /mailman/listinfo/administration HTTP/1.1" 200 6459 -lordgun.org - - [07/Mar/2004:17:01:53 -0800] "GET /razor.html HTTP/1.1" 200 2869 -64.242.88.10 - - [07/Mar/2004:17:09:01 -0800] "GET /twiki/bin/search/Main/SearchResult?scope=text®ex=on&search=Joris%20*Benschop[^A-Za-z] HTTP/1.1" 200 4284 -64.242.88.10 - - [07/Mar/2004:17:10:20 -0800] "GET /twiki/bin/oops/TWiki/TextFormattingRules?template=oopsmore¶m1=1.37¶m2=1.37 HTTP/1.1" 200 11400 -64.242.88.10 - - [07/Mar/2004:17:13:50 -0800] "GET /twiki/bin/edit/TWiki/DefaultPlugin?t=1078688936 HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:17:16:00 -0800] "GET /twiki/bin/search/Main/?scope=topic®ex=on&search=^g HTTP/1.1" 200 3675 -64.242.88.10 - - [07/Mar/2004:17:17:27 -0800] "GET /twiki/bin/search/TWiki/?scope=topic®ex=on&search=^d HTTP/1.1" 200 5773 -lj1036.inktomisearch.com - - [07/Mar/2004:17:18:36 -0800] "GET /robots.txt HTTP/1.0" 200 68 -lj1090.inktomisearch.com - - [07/Mar/2004:17:18:41 -0800] "GET /twiki/bin/view/Main/LondonOffice HTTP/1.0" 200 3860 -64.242.88.10 - - [07/Mar/2004:17:21:44 -0800] "GET /twiki/bin/attach/TWiki/TablePlugin HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:17:22:49 -0800] "GET /twiki/bin/view/TWiki/ManagingWebs?rev=1.22 HTTP/1.1" 200 9310 -64.242.88.10 - - [07/Mar/2004:17:23:54 -0800] "GET /twiki/bin/statistics/Main HTTP/1.1" 200 808 -64.242.88.10 - - [07/Mar/2004:17:26:30 -0800] "GET /twiki/bin/view/TWiki/WikiCulture HTTP/1.1" 200 5935 -64.242.88.10 - - [07/Mar/2004:17:27:37 -0800] "GET /twiki/bin/edit/Main/WebSearch?t=1078669682 HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:17:28:45 -0800] "GET /twiki/bin/oops/TWiki/ResetPassword?template=oopsmore¶m1=1.4¶m2=1.4 HTTP/1.1" 200 11281 -64.242.88.10 - - [07/Mar/2004:17:29:59 -0800] "GET /twiki/bin/view/TWiki/ManagingWebs?skin=print HTTP/1.1" 200 8806 -64.242.88.10 - - [07/Mar/2004:17:31:39 -0800] "GET /twiki/bin/edit/Main/UvscanAndPostFix?topicparent=Main.WebHome HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:17:35:35 -0800] "GET /twiki/bin/view/TWiki/KlausWriessnegger HTTP/1.1" 200 3848 -64.242.88.10 - - [07/Mar/2004:17:39:39 -0800] "GET /twiki/bin/view/Main/SpamAssassin HTTP/1.1" 200 4081 -64.242.88.10 - - [07/Mar/2004:17:42:15 -0800] "GET /twiki/bin/oops/TWiki/RichardDonkin?template=oopsmore¶m1=1.2¶m2=1.2 HTTP/1.1" 200 11281 -64.242.88.10 - - [07/Mar/2004:17:46:17 -0800] "GET /twiki/bin/rdiff/TWiki/AlWilliams?rev1=1.3&rev2=1.2 HTTP/1.1" 200 4485 -64.242.88.10 - - [07/Mar/2004:17:47:43 -0800] "GET /twiki/bin/rdiff/TWiki/AlWilliams?rev1=1.2&rev2=1.1 HTTP/1.1" 200 5234 -64.242.88.10 - - [07/Mar/2004:17:50:44 -0800] "GET /twiki/bin/view/TWiki/SvenDowideit HTTP/1.1" 200 3616 -64.242.88.10 - - [07/Mar/2004:17:53:45 -0800] "GET /twiki/bin/search/Main/SearchResult?scope=text®ex=on&search=Office%20*Locations[^A-Za-z] HTTP/1.1" 200 7771 diff --git a/src/Unit-1/CSharp/lesson5/README.md b/src/Unit-1/CSharp/lesson5/README.md deleted file mode 100644 index 9121ec7c5..000000000 --- a/src/Unit-1/CSharp/lesson5/README.md +++ /dev/null @@ -1,249 +0,0 @@ -# Lesson 1.5: Looking up Actors by Address with `ActorSelection` -Welcome to lesson 5! Wow, we've come a long way together. First, just **take a quick moment to appreciate that, and give yourself credit for investing your time and energy into your craft**. - -Mmm, that was nice. - -Okay, let's get on with it! - -In this lesson, we're going to learn how to decouple our actors from each other a bit and a new way of communicating between actors: `ActorSelection`. This lesson is shorter than the previous ones, now that we've laid down a solid conceptual foundation. - -## Key concepts / background -`ActorSelection` is a natural extension of actor hierarchies, which we covered in the last lesson. Now that we understand that actors live in hierarchies, it begs the question: now that actors aren't all on the same level, does this change the way they communicate? - -We know that we need a handle to an actor in order to send it a message and get it to do work. But now we have actors all over the place in this hierarchy, and don't always have a direct link (`ActorRef`) to the actor(s) we want to send messages to. - -*So how do we send a message to an actor somewhere else in the hierarchy, that we don't have a stored `ActorRef` for? What then?* - -Enter `ActorSelection`. - -### What is `ActorSelection`? -`ActorSelection` is nothing more than using an `ActorPath` to get a handle to an actor or actors so you can send them a message, without having to store their actual `ActorRef`s. - -Instead of getting a handle to an actor by creating or passing around its `ActorRef`, you're "looking up" a handle to the actor by its `ActorPath` (recall that the `ActorPath` is the address for an actor's position in the system hierarchy). It's kind of like looking someone up on Skype by their email when you don't already have their username. - -However, be aware that while `ActorSelection` is how you look up an `ActorRef`, it's not inherently a 1-1 lookup to a single actor. - -Technically, the `ActorSelection` object you get when you do a lookup does not point to a specific `ActorRef`. It's actually a handle that internally points to every `ActorRef` that matches the expression you looked up. Wildcards are supported in this expression, so it's an expression that selects 0+ actors. (More on this later.) - -An `ActorSelection` will also match two different `ActorRef`s with the same name if the first one dies and is replaced by another (not restarted, in which case it would be the same `ActorRef`). - -#### Is it an object? A process? Both? -We think of `ActorSelection` as both a process and an object: the process of looking actor(s) up by `ActorPath`, and the object returned from that lookup, which allows us to send messages to the actor(s) matched by the expression we looked up. - -### Why should I care about `ActorSelection`? -Always a great question, glad you asked! There are a number of benefits that `ActorSelection` gives you. - -#### Location transparency -What location transparency actually means is that whenever you send a message to an actor, you don't need to know where they are within an actor system, which might span hundreds of computers. You don't care if your actors are all in one process or spread across 100 machines around the world. You just have to know that actors' address (its `ActorPath`). - -Think of it like calling someone's cell phone number - you don't need to know that your friend Bob is in Seattle, Washington, USA in order to place a call to them. You just need to dial Bob's cell phone number and your cellular network provider will take care of the rest. - -The location transparency (enabled by `ActorSelection`) is essential for creating scalable systems that can handle high-availability requirements. We'll go into this more in Units 2 & 3. - -#### Loose coupling -Since you don't have to constantly be holding on to `ActorRef`s to store and pass around, your actors don't get tightly coupled to each other. Just like in object-oriented programming, this is a Very Good Thing. It means the components of your system stay loose and easily adaptable / reusable. It lowers the cost of maintaining your codebase. - -#### Dynamic behavior -Dynamic behavior is an advanced concept that we dive into in the beginning of Unit 2, but for now just be aware that the behavior of a given actor can be very flexible. This lets actors easily represent things like Finite State Machines so a small code footprint can easily handle complex situations. - -Where does `ActorSelection` come into play on this? Well, if you want to have a very dynamic and adaptable system, there are probably going to be lots of actors coming and going from the hierarchy and trying to store / pass around handles to all of them would be a real pain. `ActorSelection` lets you easily just send messages to well known addresses of the key actor(s) you need to communicate with, and not worry about getting/passing/storing handles to the things you need. - -You also can build extremely dynamic actors where not even the `ActorPath` needed to do an `ActorSelection` is hardcoded, but can instead be represented by a message that is passed into your actor. - -#### Flexible communication patterns == adaptable system -Let's run w/ this idea of adaptability, because it's important for your happiness as a developer, the resilience of your system, and the speed at which your organization can move. - -Since you don't have to couple everything together to make it work, this will speed up your development cycles. You can introduce new actors and entirely new sections into the actor hierarchy without having to go back and change everything you've already written. Your system has a much more flexible communication structure that can expand and accommodate new actors (and requirements) easily. - -#### In a nutshell: `ActorSelection` makes your system much more adaptable to change and also enables it to be more powerful. - -### Okay, got it. So when do I actually use `ActorSelection`? -#### Talking to top-level actors -The most common case we're aware of for using `ActorSelection` is to send messages to top-level actors with well known names. - -For example, let's imagine you have a top level actor that handles all authentication for your system. Other actors can send a message to that actor to find out if a user is authenticated or has permission to carry out a certain operation. Let's call this actor `AuthenticationActor`. - -Since this is a top-level actor, we now know thanks to our knowledge of hierarchies that its `ActorPath` is going to be `/user/AuthenticationActor`. Using this well-known address, **ANY** actor in the entire system can easily talk to it without needing to previously have its `ActorRef`, as in this example: - -```csharp -// send username to AuthenticationActor for authentication -// this is an "absolute" actor selection, since it starts at top-level /user/ -Context.ActorSelection("/user/AuthenticationActor").Tell(username); -``` - -> NOTE: `ActorSelection`s can be either absolute or relative. An absolute `ActorSelection` includes the root `/user/` actor in the path. However, an `ActorSelection` could also be relative, such as `Context.ActorSelection("../validationActor")`. - -#### Doing elastic processing of large data streams -A very common extension of the well-known actor pattern is talking to routers ([docs](http://getakka.net/wiki/Routing)). Routers are a more advanced topic we go in-depth on in unit 2. - -As a quick example, imagine that you had a system that needed to process a large data stream in real time. Let's say for fun that you had a very popular consumer app that had peak usage once or twice a day, like a social app that people love to use on their breaks at work. Each user is generating a flurry of activity that has to be processed and rolled up in realtime. For the sake of this example, imagine that you had an actor running per user tracking the user's activity, and feeding that data to other parts of the system. - -(*Remember: actors are cheap! It's totally reasonable from an overhead POV to create actors for every user. Last we checked, Akka.NET could run between 2.5-3 million actors per gig of RAM.*) - -There's a lot of data coming in, and you want to make sure the system stays responsive under high loads. What do you do? One good option is to create a router (or job coordinator, as some call it) and have that coordinator manage a pool of workers that do the processing. This pool of workers can then elastically expand/contract to meet the changing processing demands of the system on the fly. - -As all these per-user actors are being created and shutting down when users leave the app, how do you consistently feed the data to the right place? You send the data to the `ActorSelection`s for all the coordinators you need to hand off processing to. - -#### When not just replying -A very commonly used `ActorRef` is `Sender`, which is the `ActorRef` made available to every actor context and is a handle to the sender of the message currently being processed. In an earlier lesson, we used this inside `FileValidatorActor`'s `OnReceive` method to easily send a confirmation message back to the sender of the message that was being validated by `FileValidatorActor`. - -But what if, as part of processing a message, you need to send a message to another actor who is not the `Sender` of your current message? `ActorSelection` FTW. - -#### Reporting to multiple endpoints at once -Another common case is that you may have some piece of information you want to report to multiple other actors, that perhaps each run a stats service. Using `ActorSelection`, you could send the same piece of data as a message to all of those services at once, if they shared a similar well-known naming scheme. This is one good use case for a wildcard `ActorSelection`. - -### Caution: Don't pass `ActorSelection`s around -We encourage you NOT to pass around `ActorSelection`s as pararmeters, the way you do `ActorRef`s. The reason for this is that `ActorSelection`s can be relative instead of absolute, in which case it wouldn't produce the intended effects when passed to an actor with a different location in the hierarchy. - -### How do I make an `ActorSelection`? -Very simple: `var selection = Context.ActorSelection("/path/to/actorName")` - -> NOTE: **the path to an actor includes the name you assign to an actor when you instantiate it, NOT its class name. If you don't assign a name to an actor when you create it, the system will auto-generate a unique name for you**. For example: - -```csharp -class FooActor : UntypedActor {} -Props props = Props.Create(); - -// the ActorPath for myFooActor is "/user/barBazActor" -// NOT "/user/myFooActor" or "/user/FooActor" -ActorRef myFooActor = MyActorSystem.ActorOf(props, "barBazActor"); - -// if you don't specify a name on creation as below, the system will -// auto generate a name for you, so the actor path will -// be something like "/user/$a" -ActorRef myFooActor = MyActorSystem.ActorOf(props); -``` - -### Do I send a message differently to an `ActorSelection` vs an `ActorRef`? -Nope. You `Tell()` an `ActorSelection` a message just the same as an `ActorRef`: - -```csharp -var selection = Context.ActorSelection("/path/to/actorName"); -selection.Tell(message); -``` - -## Exercise -Alright, let's get to it. This exercise will be short. We are only making some small optimizations to our system. - -### Phase 1: Decouple `ConsoleReaderActor` and `FileValidatorActor` -Right now, our `ConsoleReaderActor` needs to be given an `ActorRef` to be able to send the messages it reads from the console off for validation. In the current design, that's easy enough. - -BUT, imagine that `ConsoleReaderActor` was far away in the hierarchy from where the `FileValidatorActor` instance was created (`Program.cs` currently). In this case, there is no clean/easy way to pass in the needed `ActorRef` to `ConsoleReaderActor` without also passing it through every intermediary first. - -Without `ActorSelection`, you'd have to pass the necessary `ActorRef` through every object between where the handle is created and used. That is coupling more and more objects together--**yuck**! - -Let's fix that by **removing the `validationActor` `ActorRef` that we're passing in**. The top of `ConsoleReaderActor` should now look like this: - -```csharp -// ConsoleReaderActor.cs -// note: we don't even need our own constructor anymore! -public const string StartCommand = "start"; -public const string ExitCommand = "exit"; - -protected override void OnReceive(object message) -{ - if (message.Equals(StartCommand)) - { - DoPrintInstructions(); - } - - GetAndValidateInput(); -} -``` - -Then, let's update the call for message validation inside `ConsoleReaderActor` so that the actor doesn't have to hold onto a specific `ActorRef` and can just forward the message read from the console onto an `ActorPath` where it knows validation occurs. - -```csharp -// ConsoleReaderActor.GetAndValidateInput - -// otherwise, just send the message off for validation -Context.ActorSelection("/user/validationActor").Tell(message); -``` - -Finally, let's update `consoleReaderProps` accordingly in `Program.cs` since its constructor no longer takes any arguments: -```csharp -// Program.Main -Props consoleReaderProps = Props.Create(); -``` - -### Phase 2: Decouple `FileValidatorActor` and `TailCoordinatorActor` -Just as with `ConsoleReaderActor` and `FileValidatorActor`, the `FileValidatorActor` currently requires an `ActorRef` for the `TailCoordinatorActor` which it does not need. Let's fix that. - -First, **remove the `tailCoordinatorActor` argument to the constructor of `FileValidatorActor` and remove the accompanying field on the class**. The top of `FileValidatorActor.cs` should now look like this: - -```csharp -// FileValidatorActor.cs -// note that we're no longer storing _tailCoordinatorActor field -private readonly ActorRef _consoleWriterActor; - -public FileValidatorActor(ActorRef consoleWriterActor) -{ - _consoleWriterActor = consoleWriterActor; -} -``` - -Then, let's use `ActorSelection` to communicate between `FileValidatorActor` and `TailCoordinatorActor`! Update `FileValidatorActor` like this: -```csharp -// FileValidatorActor.cs -// start coordinator -Context.ActorSelection("/user/tailCoordinatorActor").Tell(new TailCoordinatorActor.StartTail(msg, _consoleWriterActor)); -``` - -And finally, let's update `fileValidatorProps` in `Program.cs` to reflect the different constructor arguments: - -```csharp -// Program.Main -Props fileValidatorActorProps = Props.Create(() => new FileValidatorActor(consoleWriterActor)); -``` - -### Phase 3: Build and Run! -Awesome! It's time to fire this baby up and see it in action. - -Just as with the last lesson, you should be able to hit `F5` and run your log/text file and see additions to it appear in your console. - -![Petabridge Akka.NET Bootcamp Actor Selection Working](Images/selection_working.png) - -### Hey, wait, go back! What about that `consoleWriterActor` passed to `FileValidatorActor`? Wasn't that unnecessarily coupling actors? -Oh. You're good, you. - -We assume you're talking about this `ActorRef` that is still getting passed into `FileValidatorActor`: - -```csharp -// FileValidatorActor.cs -private readonly ActorRef _consoleWriterActor; - -public FileValidatorActor(ActorRef consoleWriterActor) -{ - _consoleWriterActor = consoleWriterActor; -} -``` - -*This one is a little counter-intuitive*. Here's the deal. - -In this case, we aren't using the handle for `consoleWriterActor` to talk directly to it. Instead we are putting that `ActorRef` inside a message that is getting sent somewhere else in the system for processing. When that message is received, the receiving actor will know everything it needs to in order to do its job. - -This is actually a good design pattern in the actor model, because it makes the message being passed entirely self-contained and keeps the system as a whole flexible, even if this one actor (`FileValidatorActor`) needs an `ActorRef` passed in and is a little coupled. - -Think about what is happening in the `TailCoordinatorActor` which is receiving this message: the job of the `TailCoordinatorActor` is to manage `TailActor`s which will actually observe and report file changes to... somewhere. We get to specify that somewhere up front. - -`TailActor` should not have the reporting output location written directly into it. The reporting output location is a task-level detail that should be encapsulated as an instruction within the incoming message. In this case, that task is our custom `StartTail` message, which indeed contains the `ActorRef` for the previously mentioned `consoleWriterActor` as the `reporterActor`. - -So, a little counter-intuitively, this pattern actually promotes loose coupling. You'll see it a lot as you go through Akka.NET, especially given the widespread use of the pattern of turning events into messages. - -### Once you're done -Compare your code to the solution in the [Completed](Completed/) folder to see what the instructors included in their samples. - -## Great job! Almost Done! Onto Lesson 6! -Awesome work! Well done on completing this lesson! We're on the home stretch of Unit 1, and you're doing awesome. - - -**Let's move onto [Lesson 6 - The Actor Lifecycle](../lesson6).** - - -## Any questions? -**Don't be afraid to ask questions** :). - -Come ask any questions you have, big or small, [in this ongoing Bootcamp chat with the Petabridge & Akka.NET teams](https://gitter.im/petabridge/akka-bootcamp). - -### Problems with the code? -If there is a problem with the code running, or something else that needs to be fixed in this lesson, please [create an issue](/issues) and we'll get right on it. This will benefit everyone going through Bootcamp. diff --git a/src/Unit-1/CSharp/lesson6/Completed/App.config b/src/Unit-1/CSharp/lesson6/Completed/App.config deleted file mode 100644 index 8e1564635..000000000 --- a/src/Unit-1/CSharp/lesson6/Completed/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson6/Completed/ConsoleReaderActor.cs b/src/Unit-1/CSharp/lesson6/Completed/ConsoleReaderActor.cs deleted file mode 100644 index ac2e4e101..000000000 --- a/src/Unit-1/CSharp/lesson6/Completed/ConsoleReaderActor.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor responsible for reading FROM the console. - /// Also responsible for calling . - /// - class ConsoleReaderActor : UntypedActor - { - public const string StartCommand = "start"; - public const string ExitCommand = "exit"; - - protected override void OnReceive(object message) - { - if (message.Equals(StartCommand)) - { - DoPrintInstructions(); - } - - GetAndValidateInput(); - } - - - #region Internal methods - private void DoPrintInstructions() - { - Console.WriteLine("Please provide the URI of a log file on disk.\n"); - } - - - /// - /// Reads input from console, validates it, then signals appropriate response - /// (continue processing, error, success, etc.). - /// - private void GetAndValidateInput() - { - var message = Console.ReadLine(); - if (!string.IsNullOrEmpty(message) && String.Equals(message, ExitCommand, StringComparison.OrdinalIgnoreCase)) - { - // if user typed ExitCommand, shut down the entire actor system (allows the process to exit) - Context.System.Shutdown(); - return; - } - - // otherwise, just send the message off for validation - Context.ActorSelection("/user/validationActor").Tell(message); - } - #endregion - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson6/Completed/ConsoleWriterActor.cs b/src/Unit-1/CSharp/lesson6/Completed/ConsoleWriterActor.cs deleted file mode 100644 index 05efe1a08..000000000 --- a/src/Unit-1/CSharp/lesson6/Completed/ConsoleWriterActor.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor responsible for serializing message writes to the console. - /// (write one message at a time, champ :) - /// - class ConsoleWriterActor : UntypedActor - { - protected override void OnReceive(object message) - { - if (message is Messages.InputError) - { - var msg = message as Messages.InputError; - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine(msg.Reason); - } - else if (message is Messages.InputSuccess) - { - var msg = message as Messages.InputSuccess; - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine(msg.Reason); - } - else - { - Console.WriteLine(message); - } - - Console.ResetColor(); - } - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson6/Completed/FileObserver.cs b/src/Unit-1/CSharp/lesson6/Completed/FileObserver.cs deleted file mode 100644 index a675e8fbf..000000000 --- a/src/Unit-1/CSharp/lesson6/Completed/FileObserver.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.IO; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Turns events about a specific file into messages for . - /// - public class FileObserver : IDisposable - { - private readonly ActorRef _tailActor; - private readonly string _absoluteFilePath; - private FileSystemWatcher _watcher; - private readonly string _fileDir; - private readonly string _fileNameOnly; - - public FileObserver(ActorRef tailActor, string absoluteFilePath) - { - _tailActor = tailActor; - _absoluteFilePath = absoluteFilePath; - _fileDir = Path.GetDirectoryName(absoluteFilePath); - _fileNameOnly = Path.GetFileName(absoluteFilePath); - } - - /// - /// Begin monitoring file. - /// - public void Start() - { - // make watcher to observe our specific file - _watcher = new FileSystemWatcher(_fileDir, _fileNameOnly); - - // watch our file for changes to the file name, or new messages being written to file - _watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite; - - // assign callbacks for event types - _watcher.Changed += OnFileChanged; - _watcher.Error += OnFileError; - - // start watching - _watcher.EnableRaisingEvents = true; - - } - - /// - /// Stop monitoring file. - /// - public void Dispose() - { - _watcher.Dispose(); - } - - /// - /// Callback for file error events. - /// - /// - /// - void OnFileError(object sender, ErrorEventArgs e) - { - _tailActor.Tell(new TailActor.FileError(_fileNameOnly, e.GetException().Message), ActorRef.NoSender); - } - - /// - /// Callback for file change events. - /// - /// - /// - void OnFileChanged(object sender, FileSystemEventArgs e) - { - if (e.ChangeType == WatcherChangeTypes.Changed) - { - // here we use a special ActorRef.NoSender - // since this event can happen many times, this is a little microoptimization - _tailActor.Tell(new TailActor.FileWrite(e.Name), ActorRef.NoSender); - } - - } - - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson6/Completed/FileValidatorActor.cs b/src/Unit-1/CSharp/lesson6/Completed/FileValidatorActor.cs deleted file mode 100644 index 0b60cb27a..000000000 --- a/src/Unit-1/CSharp/lesson6/Completed/FileValidatorActor.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.IO; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Actor that validates user input and signals result to others. - /// - public class FileValidatorActor : UntypedActor - { - private readonly ActorRef _consoleWriterActor; - - public FileValidatorActor(ActorRef consoleWriterActor) - { - _consoleWriterActor = consoleWriterActor; - } - - protected override void OnReceive(object message) - { - var msg = message as string; - if (string.IsNullOrEmpty(msg)) - { - // signal that the user needs to supply an input - _consoleWriterActor.Tell(new Messages.NullInputError("Input was blank. Please try again.\n")); - - // tell sender to continue doing its thing (whatever that may be, this actor doesn't care) - Sender.Tell(new Messages.ContinueProcessing()); - } - else - { - var valid = IsFileUri(msg); - if (valid) - { - // signal successful input - _consoleWriterActor.Tell(new Messages.InputSuccess(string.Format("Starting processing for {0}", msg))); - - // start coordinator - Context.ActorSelection("/user/tailCoordinatorActor").Tell(new TailCoordinatorActor.StartTail(msg, _consoleWriterActor)); - } - else - { - // signal that input was bad - _consoleWriterActor.Tell(new Messages.ValidationError(string.Format("{0} is not an existing URI on disk.", msg))); - - // tell sender to continue doing its thing (whatever that may be, this actor doesn't care) - Sender.Tell(new Messages.ContinueProcessing()); - } - } - - - - } - - /// - /// Checks if file exists at path provided by user. - /// - /// - /// - private static bool IsFileUri(string path) - { - return File.Exists(path); - } - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson6/Completed/Messages.cs b/src/Unit-1/CSharp/lesson6/Completed/Messages.cs deleted file mode 100644 index f322a76ed..000000000 --- a/src/Unit-1/CSharp/lesson6/Completed/Messages.cs +++ /dev/null @@ -1,59 +0,0 @@ -namespace WinTail -{ - class Messages - { - - #region Neutral/system messages - /// - /// Marker class to continue processing. - /// - public class ContinueProcessing { } - #endregion - - #region Success messages - /// - /// Base class for signalling that user input was valid. - /// - public class InputSuccess - { - public InputSuccess(string reason) - { - Reason = reason; - } - - public string Reason { get; private set; } - } - #endregion - - #region Error messages - /// - /// Base class for signalling that user input was invalid. - /// - public class InputError - { - public InputError(string reason) - { - Reason = reason; - } - - public string Reason { get; private set; } - } - - /// - /// User provided blank input. - /// - public class NullInputError : InputError - { - public NullInputError(string reason) : base(reason) { } - } - - /// - /// User provided invalid input (currently, input w/ odd # chars) - /// - public class ValidationError : InputError - { - public ValidationError(string reason) : base(reason) { } - } - #endregion - } -} diff --git a/src/Unit-1/CSharp/lesson6/Completed/Program.cs b/src/Unit-1/CSharp/lesson6/Completed/Program.cs deleted file mode 100644 index da2c049b2..000000000 --- a/src/Unit-1/CSharp/lesson6/Completed/Program.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Akka.Actor; - -namespace WinTail -{ - class Program - { - public static ActorSystem MyActorSystem; - - static void Main(string[] args) - { - // make actor system - MyActorSystem = ActorSystem.Create("MyActorSystem"); - - // create top-level actors within the actor system - Props consoleWriterProps = Props.Create(); - ActorRef consoleWriterActor = MyActorSystem.ActorOf(consoleWriterProps, "consoleWriterActor"); - - Props tailCoordinatorProps = Props.Create(() => new TailCoordinatorActor()); - ActorRef tailCoordinatorActor = MyActorSystem.ActorOf(tailCoordinatorProps, "tailCoordinatorActor"); - - Props fileValidatorActorProps = Props.Create(() => new FileValidatorActor(consoleWriterActor)); - ActorRef fileValidatorActor = MyActorSystem.ActorOf(fileValidatorActorProps, "validationActor"); - - Props consoleReaderProps = Props.Create(); - ActorRef consoleReaderActor = MyActorSystem.ActorOf(consoleReaderProps, "consoleReaderActor"); - - // begin processing - consoleReaderActor.Tell(ConsoleReaderActor.StartCommand); - - // blocks the main thread from exiting until the actor system is shut down - MyActorSystem.AwaitTermination(); - } - - } -} diff --git a/src/Unit-1/CSharp/lesson6/Completed/Properties/AssemblyInfo.cs b/src/Unit-1/CSharp/lesson6/Completed/Properties/AssemblyInfo.cs deleted file mode 100644 index 6caf4a85a..000000000 --- a/src/Unit-1/CSharp/lesson6/Completed/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Lesson1-1")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Lesson1-1")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("60155426-eaa6-430b-bac5-1a0c7dc0cfcb")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Unit-1/CSharp/lesson6/Completed/TailActor.cs b/src/Unit-1/CSharp/lesson6/Completed/TailActor.cs deleted file mode 100644 index 291bf1871..000000000 --- a/src/Unit-1/CSharp/lesson6/Completed/TailActor.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System.IO; -using System.Text; -using Akka.Actor; - -namespace WinTail -{ - /// - /// Monitors the file at for changes and sends file updates to console. - /// - public class TailActor : UntypedActor - { - #region Message types - - /// - /// Signal that the file has changed, and we need to read the next line of the file. - /// - public class FileWrite - { - public FileWrite(string fileName) - { - FileName = fileName; - } - - public string FileName { get; private set; } - } - - /// - /// Signal that the OS had an error accessing the file. - /// - public class FileError - { - public FileError(string fileName, string reason) - { - FileName = fileName; - Reason = reason; - } - - public string FileName { get; private set; } - - public string Reason { get; private set; } - } - - /// - /// Signal to read the initial contents of the file at actor startup. - /// - public class InitialRead - { - public InitialRead(string fileName, string text) - { - FileName = fileName; - Text = text; - } - - public string FileName { get; private set; } - public string Text { get; private set; } - } - - #endregion - - private readonly string _filePath; - private readonly ActorRef _reporterActor; - private FileObserver _observer; - private Stream _fileStream; - private StreamReader _fileStreamReader; - - public TailActor(ActorRef reporterActor, string filePath) - { - _reporterActor = reporterActor; - _filePath = filePath; - } - - /// - /// Initialization logic for actor that will tail changes to a file. - /// - protected override void PreStart() - { - // start watching file for changes - _observer = new FileObserver(Self, Path.GetFullPath(_filePath)); - _observer.Start(); - - // open the file stream with shared read/write permissions (so file can be written to while open) - _fileStream = new FileStream(Path.GetFullPath(_filePath), FileMode.Open, FileAccess.Read, - FileShare.ReadWrite); - _fileStreamReader = new StreamReader(_fileStream, Encoding.UTF8); - - // read the initial contents of the file and send it to console as first message - var text = _fileStreamReader.ReadToEnd(); - Self.Tell(new InitialRead(_filePath, text)); - } - - /// - /// Cleanup OS handles for and . - /// - protected override void PostStop() - { - _observer.Dispose(); - _observer = null; - _fileStreamReader.Close(); - _fileStreamReader.Dispose(); - base.PostStop(); - } - - protected override void OnReceive(object message) - { - if (message is FileWrite) - { - // move file cursor forward - // pull results from cursor to end of file and write to output - // (tis is assuming a log file type format that is append-only) - var text = _fileStreamReader.ReadToEnd(); - if (!string.IsNullOrEmpty(text)) - { - _reporterActor.Tell(text); - } - - } - else if (message is FileError) - { - var fe = message as FileError; - _reporterActor.Tell(string.Format("Tail error: {0}", fe.Reason)); - } - else if (message is InitialRead) - { - var ir = message as InitialRead; - _reporterActor.Tell(ir.Text); - } - } - } -} \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson6/Completed/TailCoordinatorActor.cs b/src/Unit-1/CSharp/lesson6/Completed/TailCoordinatorActor.cs deleted file mode 100644 index c1ee2e0c7..000000000 --- a/src/Unit-1/CSharp/lesson6/Completed/TailCoordinatorActor.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using Akka.Actor; - -namespace WinTail -{ - public class TailCoordinatorActor : UntypedActor - { - #region Message types - /// - /// Start tailing the file at user-specified path. - /// - public class StartTail - { - public StartTail(string filePath, ActorRef reporterActor) - { - FilePath = filePath; - ReporterActor = reporterActor; - } - - public string FilePath { get; private set; } - - public ActorRef ReporterActor { get; private set; } - } - - /// - /// Stop tailing the file at user-specified path. - /// - public class StopTail - { - public StopTail(string filePath) - { - FilePath = filePath; - } - - public string FilePath { get; private set; } - } - - #endregion - - protected override void OnReceive(object message) - { - if (message is StartTail) - { - var msg = message as StartTail; - Context.ActorOf(Props.Create(() => new TailActor(msg.ReporterActor, msg.FilePath))); - } - - } - - // here we are overriding the default SupervisorStrategy - // which is a One-For-One strategy w/ a Restart directive - protected override SupervisorStrategy SupervisorStrategy() - { - return new OneForOneStrategy ( - 10, // maxNumberOfRetries - TimeSpan.FromSeconds(30), // duration - decider: x => - { - //Maybe we consider ArithmeticException to not be application critical - //so we just ignore the error and keep going. - if (x is ArithmeticException) return Directive.Resume; - - //Error that we cannot recover from, stop the failing actor - else if (x is NotSupportedException) return Directive.Stop; - - //In all other cases, just restart the failing actor - else return Directive.Restart; - }); - } - } -} - - diff --git a/src/Unit-1/CSharp/lesson6/Completed/WinTail.csproj b/src/Unit-1/CSharp/lesson6/Completed/WinTail.csproj deleted file mode 100644 index 8550da4db..000000000 --- a/src/Unit-1/CSharp/lesson6/Completed/WinTail.csproj +++ /dev/null @@ -1,73 +0,0 @@ - - - - - Debug - AnyCPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F} - Exe - Properties - WinTail - Lesson1-1 - v4.5 - 512 - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - packages\Akka.0.8.0\lib\net45\Akka.dll - - - False - packages\Newtonsoft.Json.6.0.1\lib\net45\Newtonsoft.Json.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson6/Completed/WinTail.sln b/src/Unit-1/CSharp/lesson6/Completed/WinTail.sln deleted file mode 100644 index 1c0539943..000000000 --- a/src/Unit-1/CSharp/lesson6/Completed/WinTail.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinTail", "WinTail.csproj", "{E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E8D9CD24-DBF5-4524-AC3D-B7B2995FBA1F}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/Unit-1/CSharp/lesson6/Completed/packages.config b/src/Unit-1/CSharp/lesson6/Completed/packages.config deleted file mode 100644 index 88dee0f6e..000000000 --- a/src/Unit-1/CSharp/lesson6/Completed/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/Unit-1/CSharp/lesson6/Completed/sample_log_file.txt b/src/Unit-1/CSharp/lesson6/Completed/sample_log_file.txt deleted file mode 100644 index b6a9efb78..000000000 --- a/src/Unit-1/CSharp/lesson6/Completed/sample_log_file.txt +++ /dev/null @@ -1,50 +0,0 @@ -64.242.88.10 - - [07/Mar/2004:16:05:49 -0800] "GET /twiki/bin/edit/Main/Double_bounce_sender?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:16:06:51 -0800] "GET /twiki/bin/rdiff/TWiki/NewUserTemplate?rev1=1.3&rev2=1.2 HTTP/1.1" 200 4523 -64.242.88.10 - - [07/Mar/2004:16:10:02 -0800] "GET /mailman/listinfo/hsdivision HTTP/1.1" 200 6291 -64.242.88.10 - - [07/Mar/2004:16:11:58 -0800] "GET /twiki/bin/view/TWiki/WikiSyntax HTTP/1.1" 200 7352 -64.242.88.10 - - [07/Mar/2004:16:20:55 -0800] "GET /twiki/bin/view/Main/DCCAndPostFix HTTP/1.1" 200 5253 -64.242.88.10 - - [07/Mar/2004:16:23:12 -0800] "GET /twiki/bin/oops/TWiki/AppendixFileSystem?template=oopsmore¶m1=1.12¶m2=1.12 HTTP/1.1" 200 11382 -64.242.88.10 - - [07/Mar/2004:16:24:16 -0800] "GET /twiki/bin/view/Main/PeterThoeny HTTP/1.1" 200 4924 -64.242.88.10 - - [07/Mar/2004:16:29:16 -0800] "GET /twiki/bin/edit/Main/Header_checks?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12851 -64.242.88.10 - - [07/Mar/2004:16:30:29 -0800] "GET /twiki/bin/attach/Main/OfficeLocations HTTP/1.1" 401 12851 -64.242.88.10 - - [07/Mar/2004:16:31:48 -0800] "GET /twiki/bin/view/TWiki/WebTopicEditTemplate HTTP/1.1" 200 3732 -64.242.88.10 - - [07/Mar/2004:16:32:50 -0800] "GET /twiki/bin/view/Main/WebChanges HTTP/1.1" 200 40520 -64.242.88.10 - - [07/Mar/2004:16:33:53 -0800] "GET /twiki/bin/edit/Main/Smtpd_etrn_restrictions?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12851 -64.242.88.10 - - [07/Mar/2004:16:35:19 -0800] "GET /mailman/listinfo/business HTTP/1.1" 200 6379 -64.242.88.10 - - [07/Mar/2004:16:36:22 -0800] "GET /twiki/bin/rdiff/Main/WebIndex?rev1=1.2&rev2=1.1 HTTP/1.1" 200 46373 -64.242.88.10 - - [07/Mar/2004:16:37:27 -0800] "GET /twiki/bin/view/TWiki/DontNotify HTTP/1.1" 200 4140 -64.242.88.10 - - [07/Mar/2004:16:39:24 -0800] "GET /twiki/bin/view/Main/TokyoOffice HTTP/1.1" 200 3853 -64.242.88.10 - - [07/Mar/2004:16:43:54 -0800] "GET /twiki/bin/view/Main/MikeMannix HTTP/1.1" 200 3686 -64.242.88.10 - - [07/Mar/2004:16:45:56 -0800] "GET /twiki/bin/attach/Main/PostfixCommands HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:16:47:12 -0800] "GET /robots.txt HTTP/1.1" 200 68 -64.242.88.10 - - [07/Mar/2004:16:47:46 -0800] "GET /twiki/bin/rdiff/Know/ReadmeFirst?rev1=1.5&rev2=1.4 HTTP/1.1" 200 5724 -64.242.88.10 - - [07/Mar/2004:16:49:04 -0800] "GET /twiki/bin/view/Main/TWikiGroups?rev=1.2 HTTP/1.1" 200 5162 -64.242.88.10 - - [07/Mar/2004:16:50:54 -0800] "GET /twiki/bin/rdiff/Main/ConfigurationVariables HTTP/1.1" 200 59679 -64.242.88.10 - - [07/Mar/2004:16:52:35 -0800] "GET /twiki/bin/edit/Main/Flush_service_name?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12851 -64.242.88.10 - - [07/Mar/2004:16:53:46 -0800] "GET /twiki/bin/rdiff/TWiki/TWikiRegistration HTTP/1.1" 200 34395 -64.242.88.10 - - [07/Mar/2004:16:54:55 -0800] "GET /twiki/bin/rdiff/Main/NicholasLee HTTP/1.1" 200 7235 -64.242.88.10 - - [07/Mar/2004:16:56:39 -0800] "GET /twiki/bin/view/Sandbox/WebHome?rev=1.6 HTTP/1.1" 200 8545 -64.242.88.10 - - [07/Mar/2004:16:58:54 -0800] "GET /mailman/listinfo/administration HTTP/1.1" 200 6459 -lordgun.org - - [07/Mar/2004:17:01:53 -0800] "GET /razor.html HTTP/1.1" 200 2869 -64.242.88.10 - - [07/Mar/2004:17:09:01 -0800] "GET /twiki/bin/search/Main/SearchResult?scope=text®ex=on&search=Joris%20*Benschop[^A-Za-z] HTTP/1.1" 200 4284 -64.242.88.10 - - [07/Mar/2004:17:10:20 -0800] "GET /twiki/bin/oops/TWiki/TextFormattingRules?template=oopsmore¶m1=1.37¶m2=1.37 HTTP/1.1" 200 11400 -64.242.88.10 - - [07/Mar/2004:17:13:50 -0800] "GET /twiki/bin/edit/TWiki/DefaultPlugin?t=1078688936 HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:17:16:00 -0800] "GET /twiki/bin/search/Main/?scope=topic®ex=on&search=^g HTTP/1.1" 200 3675 -64.242.88.10 - - [07/Mar/2004:17:17:27 -0800] "GET /twiki/bin/search/TWiki/?scope=topic®ex=on&search=^d HTTP/1.1" 200 5773 -lj1036.inktomisearch.com - - [07/Mar/2004:17:18:36 -0800] "GET /robots.txt HTTP/1.0" 200 68 -lj1090.inktomisearch.com - - [07/Mar/2004:17:18:41 -0800] "GET /twiki/bin/view/Main/LondonOffice HTTP/1.0" 200 3860 -64.242.88.10 - - [07/Mar/2004:17:21:44 -0800] "GET /twiki/bin/attach/TWiki/TablePlugin HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:17:22:49 -0800] "GET /twiki/bin/view/TWiki/ManagingWebs?rev=1.22 HTTP/1.1" 200 9310 -64.242.88.10 - - [07/Mar/2004:17:23:54 -0800] "GET /twiki/bin/statistics/Main HTTP/1.1" 200 808 -64.242.88.10 - - [07/Mar/2004:17:26:30 -0800] "GET /twiki/bin/view/TWiki/WikiCulture HTTP/1.1" 200 5935 -64.242.88.10 - - [07/Mar/2004:17:27:37 -0800] "GET /twiki/bin/edit/Main/WebSearch?t=1078669682 HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:17:28:45 -0800] "GET /twiki/bin/oops/TWiki/ResetPassword?template=oopsmore¶m1=1.4¶m2=1.4 HTTP/1.1" 200 11281 -64.242.88.10 - - [07/Mar/2004:17:29:59 -0800] "GET /twiki/bin/view/TWiki/ManagingWebs?skin=print HTTP/1.1" 200 8806 -64.242.88.10 - - [07/Mar/2004:17:31:39 -0800] "GET /twiki/bin/edit/Main/UvscanAndPostFix?topicparent=Main.WebHome HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:17:35:35 -0800] "GET /twiki/bin/view/TWiki/KlausWriessnegger HTTP/1.1" 200 3848 -64.242.88.10 - - [07/Mar/2004:17:39:39 -0800] "GET /twiki/bin/view/Main/SpamAssassin HTTP/1.1" 200 4081 -64.242.88.10 - - [07/Mar/2004:17:42:15 -0800] "GET /twiki/bin/oops/TWiki/RichardDonkin?template=oopsmore¶m1=1.2¶m2=1.2 HTTP/1.1" 200 11281 -64.242.88.10 - - [07/Mar/2004:17:46:17 -0800] "GET /twiki/bin/rdiff/TWiki/AlWilliams?rev1=1.3&rev2=1.2 HTTP/1.1" 200 4485 -64.242.88.10 - - [07/Mar/2004:17:47:43 -0800] "GET /twiki/bin/rdiff/TWiki/AlWilliams?rev1=1.2&rev2=1.1 HTTP/1.1" 200 5234 -64.242.88.10 - - [07/Mar/2004:17:50:44 -0800] "GET /twiki/bin/view/TWiki/SvenDowideit HTTP/1.1" 200 3616 -64.242.88.10 - - [07/Mar/2004:17:53:45 -0800] "GET /twiki/bin/search/Main/SearchResult?scope=text®ex=on&search=Office%20*Locations[^A-Za-z] HTTP/1.1" 200 7771 diff --git a/src/Unit-1/CSharp/lesson6/Images/Thumbs.db b/src/Unit-1/CSharp/lesson6/Images/Thumbs.db deleted file mode 100644 index da9755763..000000000 Binary files a/src/Unit-1/CSharp/lesson6/Images/Thumbs.db and /dev/null differ diff --git a/src/Unit-1/CSharp/lesson6/Images/lifecycle_methods.png b/src/Unit-1/CSharp/lesson6/Images/lifecycle_methods.png deleted file mode 100644 index 6f7348822..000000000 Binary files a/src/Unit-1/CSharp/lesson6/Images/lifecycle_methods.png and /dev/null differ diff --git a/src/Unit-1/CSharp/lesson6/README.md b/src/Unit-1/CSharp/lesson6/README.md deleted file mode 100644 index cf30ae7c1..000000000 --- a/src/Unit-1/CSharp/lesson6/README.md +++ /dev/null @@ -1,194 +0,0 @@ -# Lesson 1.6: The Actor Lifecycle -Wow! Look at you--made it all the way to the end of Unit 1! Congratulations. Seriously. We appreciate and commend you on your dedication to learning and growing as a developer. - -This last lesson will wrap up our "fundamentals series" on working with actors, and it ends with a critical concept: actor life cycle. - -## Key concepts / background -### What is the actor life cycle? -Actors have a well-defined life cycle. Actors are created and started, and then they spend most of their lives receiving messages. In the event that you no longer need an actor, you can terminate or "stop" an actor. - -### What are the stages of the actor life cycle? -There are 5 stages of the actor life cycle in Akka.NET: -1. `Starting` -2. `Receiving` -3. `Stopping` -4. `Terminated`, or -5. `Restarting` - -![Akka.NET actor life cycle steps with explicit methods](Images/lifecycle.png) - -Let's take them in turn. - -#### `Starting` -The actor is waking up! This is the initial state of the actor, when it is being initialized by the `ActorSystem`. - -#### `Receiving` -The actor is now available to process messages. Its `Mailbox` (more on that later) will begin delivering messages into the `OnReceive` method of the actor for processing. - -#### `Stopping` -During this phase, the actor is cleaning up its state. What happens during this phase depends on whether the actor is being terminated, or restarted. - -If the actor is being restarted, it's common to save state or messages during this phase to be processed once the actor is back in its Receiving state after the restart. - -If the actor is being terminated, all the messages in its `Mailbox` will be sent to the `DeadLetters` mailbox of the `ActorSystem`. `DeadLetters` is a store of undeliverable messages, usually undeliverable because an actor is dead. - -#### `Terminated` -The actor is dead. Any messages sent to its former `ActorRef` will now go to `DeadLetters` instead. The actor cannot be restarted, but a new actor can be created at its former address (which will have a new `ActorRef` but an identical `ActorPath`). - -#### `Restarting` -The actor is about to restart and go back into a `Starting` state. - -### Life cycle hook methods -So, how can you link into the actor life cycle? Here are the 4 places you can hook in. - -#### `PreStart` -`PreStart` logic gets run before the actor can begin receiving messages and is a good place to put initialization logic. Gets called during restarts too. - -#### `PreRestart` -If your actor accidentally fails (i.e. throws an unhandled Exception) the actor's parent will restart the actor. `PreRestart` is where you can hook in to do cleanup before the actor restarts, or to save the current message for reprocessing later. - -#### `PostStop` -`PostStop` is called once the actor has stopped and is no longer receiving messages. This is a good place to include clean-up logic. PostStop does not get called during actor restarts - only when an actor is being terminated. - -`DeathWatch` is also when an actor notifies any other actors that have subscribed to be alerted when it terminates. `DeathWatch` is just a pub/sub system built into framework for any actor to be alerted to the termination of any other actor. - -#### `PostRestart` -`PostRestart` is called during restarts after PreRestart but before PreStart. This is a good place to do any additional reporting or diagnosis on the error that caused the actor to crash, beyond what Akka.NET already does for you. - -Here's where the hook methods fit into the stages of the life cycle: - -![Akka.NET actor life cycle steps with explicit methods](Images/lifecycle_methods.png) - -### How do I hook into the life cycle? -To hook in, you just override the method you want to hook into, like this: - -```csharp - /// -/// Initialization logic for actor -/// -protected override void PreStart() -{ - // do whatever you need to here -} -``` - -### Which are the most commonly used life cycle methods? -#### `PreStart` -`PreStart` is far and away the most common hook method used. It is used to set up initial state for the actor and run any custom initialization logic your actor needs. - -#### `PostStop` -The second most common place to hook into the life cycle is in `PostStop`, to do custom cleanup logic. For example, you may want to make sure your actor releases file system handles or any other resources it is consuming from the system before it terminates. - -#### `PreRestart` -`PreRestart` is in a distant third to the above methods, but you will occasionally use it. What you use it for is highly dependent on what the actor does, but one common case is to stash a message or otherwise take steps to get it back for reprocessing once the actor restarts. - -### How does this relate to supervision? -In the event that an actor accidentally crashes (i.e. throws an unhandled Exception,) the actor's supervisor will automatically restart the actor's lifecycle from scratch - without losing any of the remaining messages still in the actor's mailbox. - -As we covered in lesson 4 on the actor hierarchy/supervision, what occurs in the case of an unhandled error is determined by the `SupervisionDirective` of the parent. That parent can instruct the child to terminate, restart, or ignore the error and pick up where it left off. The default is to restart, so that any bad state is blown away and the actor starts clean. Restarts are cheap. - -## Exercise -This final exercise is very short, as our system is already complete. We're just going to use it to optimize the initialization and shutdown of `TailActor`. - -### Move initialization logic from `TailActor` constructor to `PreStart()` -See all this in the constructor of `TailActor`? - -```csharp -// TailActor.cs constructor -// start watching file for changes -_observer = new FileObserver(Self, Path.GetFullPath(_filePath)); -_observer.Start(); - -// open the file stream with shared read/write permissions (so file can be written to while open) -_fileStream = new FileStream(Path.GetFullPath(_filePath), FileMode.Open, FileAccess.Read, - FileShare.ReadWrite); -_fileStreamReader = new StreamReader(_fileStream, Encoding.UTF8); - -// read the initial contents of the file and send it to console as first message -var text = _fileStreamReader.ReadToEnd(); -Self.Tell(new InitialRead(_filePath, text)); -``` - -While it works, initialization logic really belongs in the `PreStart()` method. - -Time to use your first life cycle method! - -Pull all of the above init logic out of the `TailActor` constructor and move it into `PreStart()`. We'll also need to change `_observer`, `_fileStream`, and `_fileStreamReader` to non-readonly fields since they're moving out of the constructor. - -The top of `TailActor.cs` should now look like this - -```csharp -// TailActor.cs -private FileObserver _observer; -private Stream _fileStream; -private StreamReader _fileStreamReader; - -public TailActor(ActorRef reporterActor, string filePath) -{ - _reporterActor = reporterActor; - _filePath = filePath; -} - -// we moved all the initialization logic from the constructor -// down below to PreStart! - -/// -/// Initialization logic for actor that will tail changes to a file. -/// -protected override void PreStart() -{ - // start watching file for changes - _observer = new FileObserver(Self, Path.GetFullPath(_filePath)); - _observer.Start(); - - // open the file stream with shared read/write permissions (so file can be written to while open) - _fileStream = new FileStream(Path.GetFullPath(_filePath), FileMode.Open, FileAccess.Read, - FileShare.ReadWrite); - _fileStreamReader = new StreamReader(_fileStream, Encoding.UTF8); - - // read the initial contents of the file and send it to console as first message - var text = _fileStreamReader.ReadToEnd(); - Self.Tell(new InitialRead(_filePath, text)); -} -``` - -Much better! Okay, what's next? - -### Let's clean up and take good care of our `FileSystem` resources -`TailActor` instances are each storing OS handles in `_fileStreamReader` and `FileObserver`. Let's use `PostStop()` to make sure those handles are cleaned up and we are releasing all our resources back to the OS. - -Add this to `TailActor`: - -```csharp -// TailActor.cs -/// -/// Cleanup OS handles for and . -/// -protected override void PostStop() -{ - _observer.Dispose(); - _observer = null; - _fileStreamReader.Close(); - _fileStreamReader.Dispose(); - base.PostStop(); -} -``` - -### Phase 4: Build and Run! -That's it! Hit `F5` to run the solution and it should work exactly the same as before, albeit a little more optimized. :) - -### Once you're done -Compare your code to the solution in the [Completed](Completed/) folder to see what the instructors included in their samples. - -## Great job! -### WOW! YOU WIN! Phenomenal work finishing Unit 1. - -**Ready for more? [Start Unit 2 now](../../Unit-2 "Akka.NET Bootcamp Unit 2").** - -## Any questions? -**Don't be afraid to ask questions** :). - -Come ask any questions you have, big or small, [in this ongoing Bootcamp chat with the Petabridge & Akka.NET teams](https://gitter.im/petabridge/akka-bootcamp). - -### Problems with the code? -If there is a problem with the code running, or something else that needs to be fixed in this lesson, please [create an issue](/issues) and we'll get right on it. This will benefit everyone going through Bootcamp. diff --git a/src/Unit-1/FSharp/DoThis/Actors.fs b/src/Unit-1/DoThis/Actors.fs similarity index 100% rename from src/Unit-1/FSharp/DoThis/Actors.fs rename to src/Unit-1/DoThis/Actors.fs diff --git a/src/Unit-1/FSharp/DoThis/App.config b/src/Unit-1/DoThis/App.config similarity index 100% rename from src/Unit-1/FSharp/DoThis/App.config rename to src/Unit-1/DoThis/App.config diff --git a/src/Unit-1/FSharp/DoThis/Program.fs b/src/Unit-1/DoThis/Program.fs similarity index 100% rename from src/Unit-1/FSharp/DoThis/Program.fs rename to src/Unit-1/DoThis/Program.fs diff --git a/src/Unit-1/FSharp/DoThis/WinTail.fsproj b/src/Unit-1/DoThis/WinTail.fsproj similarity index 100% rename from src/Unit-1/FSharp/DoThis/WinTail.fsproj rename to src/Unit-1/DoThis/WinTail.fsproj diff --git a/src/Unit-1/FSharp/DoThis/WinTail.sln b/src/Unit-1/DoThis/WinTail.sln similarity index 100% rename from src/Unit-1/FSharp/DoThis/WinTail.sln rename to src/Unit-1/DoThis/WinTail.sln diff --git a/src/Unit-1/FSharp/DoThis/packages.config b/src/Unit-1/DoThis/packages.config similarity index 100% rename from src/Unit-1/FSharp/DoThis/packages.config rename to src/Unit-1/DoThis/packages.config diff --git a/src/Unit-1/FSharp/lesson1/Images/Thumbs.db b/src/Unit-1/FSharp/lesson1/Images/Thumbs.db deleted file mode 100644 index a9a00fd78..000000000 Binary files a/src/Unit-1/FSharp/lesson1/Images/Thumbs.db and /dev/null differ diff --git a/src/Unit-1/FSharp/lesson1/Images/example.png b/src/Unit-1/FSharp/lesson1/Images/example.png deleted file mode 100644 index 0dd64e8de..000000000 Binary files a/src/Unit-1/FSharp/lesson1/Images/example.png and /dev/null differ diff --git a/src/Unit-1/FSharp/lesson2/Completed/.nuget/NuGet.Config b/src/Unit-1/FSharp/lesson2/Completed/.nuget/NuGet.Config deleted file mode 100644 index 67f8ea046..000000000 --- a/src/Unit-1/FSharp/lesson2/Completed/.nuget/NuGet.Config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/Unit-1/FSharp/lesson2/Completed/.nuget/NuGet.targets b/src/Unit-1/FSharp/lesson2/Completed/.nuget/NuGet.targets deleted file mode 100644 index 3f8c37b22..000000000 --- a/src/Unit-1/FSharp/lesson2/Completed/.nuget/NuGet.targets +++ /dev/null @@ -1,144 +0,0 @@ - - - - $(MSBuildProjectDirectory)\..\ - - - false - - - false - - - true - - - false - - - - - - - - - - - $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) - - - - - $(SolutionDir).nuget - - - - $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config - $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config - - - - $(MSBuildProjectDirectory)\packages.config - $(PackagesProjectConfig) - - - - - $(NuGetToolsPath)\NuGet.exe - @(PackageSource) - - "$(NuGetExePath)" - mono --runtime=v4.0.30319 "$(NuGetExePath)" - - $(TargetDir.Trim('\\')) - - -RequireConsent - -NonInteractive - - "$(SolutionDir) " - "$(SolutionDir)" - - - $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) - $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols - - - - RestorePackages; - $(BuildDependsOn); - - - - - $(BuildDependsOn); - BuildPackage; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Unit-1/FSharp/lesson4/DoThis/sample_log_file.txt b/src/Unit-1/FSharp/lesson4/DoThis/sample_log_file.txt deleted file mode 100644 index b6a9efb78..000000000 --- a/src/Unit-1/FSharp/lesson4/DoThis/sample_log_file.txt +++ /dev/null @@ -1,50 +0,0 @@ -64.242.88.10 - - [07/Mar/2004:16:05:49 -0800] "GET /twiki/bin/edit/Main/Double_bounce_sender?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:16:06:51 -0800] "GET /twiki/bin/rdiff/TWiki/NewUserTemplate?rev1=1.3&rev2=1.2 HTTP/1.1" 200 4523 -64.242.88.10 - - [07/Mar/2004:16:10:02 -0800] "GET /mailman/listinfo/hsdivision HTTP/1.1" 200 6291 -64.242.88.10 - - [07/Mar/2004:16:11:58 -0800] "GET /twiki/bin/view/TWiki/WikiSyntax HTTP/1.1" 200 7352 -64.242.88.10 - - [07/Mar/2004:16:20:55 -0800] "GET /twiki/bin/view/Main/DCCAndPostFix HTTP/1.1" 200 5253 -64.242.88.10 - - [07/Mar/2004:16:23:12 -0800] "GET /twiki/bin/oops/TWiki/AppendixFileSystem?template=oopsmore¶m1=1.12¶m2=1.12 HTTP/1.1" 200 11382 -64.242.88.10 - - [07/Mar/2004:16:24:16 -0800] "GET /twiki/bin/view/Main/PeterThoeny HTTP/1.1" 200 4924 -64.242.88.10 - - [07/Mar/2004:16:29:16 -0800] "GET /twiki/bin/edit/Main/Header_checks?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12851 -64.242.88.10 - - [07/Mar/2004:16:30:29 -0800] "GET /twiki/bin/attach/Main/OfficeLocations HTTP/1.1" 401 12851 -64.242.88.10 - - [07/Mar/2004:16:31:48 -0800] "GET /twiki/bin/view/TWiki/WebTopicEditTemplate HTTP/1.1" 200 3732 -64.242.88.10 - - [07/Mar/2004:16:32:50 -0800] "GET /twiki/bin/view/Main/WebChanges HTTP/1.1" 200 40520 -64.242.88.10 - - [07/Mar/2004:16:33:53 -0800] "GET /twiki/bin/edit/Main/Smtpd_etrn_restrictions?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12851 -64.242.88.10 - - [07/Mar/2004:16:35:19 -0800] "GET /mailman/listinfo/business HTTP/1.1" 200 6379 -64.242.88.10 - - [07/Mar/2004:16:36:22 -0800] "GET /twiki/bin/rdiff/Main/WebIndex?rev1=1.2&rev2=1.1 HTTP/1.1" 200 46373 -64.242.88.10 - - [07/Mar/2004:16:37:27 -0800] "GET /twiki/bin/view/TWiki/DontNotify HTTP/1.1" 200 4140 -64.242.88.10 - - [07/Mar/2004:16:39:24 -0800] "GET /twiki/bin/view/Main/TokyoOffice HTTP/1.1" 200 3853 -64.242.88.10 - - [07/Mar/2004:16:43:54 -0800] "GET /twiki/bin/view/Main/MikeMannix HTTP/1.1" 200 3686 -64.242.88.10 - - [07/Mar/2004:16:45:56 -0800] "GET /twiki/bin/attach/Main/PostfixCommands HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:16:47:12 -0800] "GET /robots.txt HTTP/1.1" 200 68 -64.242.88.10 - - [07/Mar/2004:16:47:46 -0800] "GET /twiki/bin/rdiff/Know/ReadmeFirst?rev1=1.5&rev2=1.4 HTTP/1.1" 200 5724 -64.242.88.10 - - [07/Mar/2004:16:49:04 -0800] "GET /twiki/bin/view/Main/TWikiGroups?rev=1.2 HTTP/1.1" 200 5162 -64.242.88.10 - - [07/Mar/2004:16:50:54 -0800] "GET /twiki/bin/rdiff/Main/ConfigurationVariables HTTP/1.1" 200 59679 -64.242.88.10 - - [07/Mar/2004:16:52:35 -0800] "GET /twiki/bin/edit/Main/Flush_service_name?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12851 -64.242.88.10 - - [07/Mar/2004:16:53:46 -0800] "GET /twiki/bin/rdiff/TWiki/TWikiRegistration HTTP/1.1" 200 34395 -64.242.88.10 - - [07/Mar/2004:16:54:55 -0800] "GET /twiki/bin/rdiff/Main/NicholasLee HTTP/1.1" 200 7235 -64.242.88.10 - - [07/Mar/2004:16:56:39 -0800] "GET /twiki/bin/view/Sandbox/WebHome?rev=1.6 HTTP/1.1" 200 8545 -64.242.88.10 - - [07/Mar/2004:16:58:54 -0800] "GET /mailman/listinfo/administration HTTP/1.1" 200 6459 -lordgun.org - - [07/Mar/2004:17:01:53 -0800] "GET /razor.html HTTP/1.1" 200 2869 -64.242.88.10 - - [07/Mar/2004:17:09:01 -0800] "GET /twiki/bin/search/Main/SearchResult?scope=text®ex=on&search=Joris%20*Benschop[^A-Za-z] HTTP/1.1" 200 4284 -64.242.88.10 - - [07/Mar/2004:17:10:20 -0800] "GET /twiki/bin/oops/TWiki/TextFormattingRules?template=oopsmore¶m1=1.37¶m2=1.37 HTTP/1.1" 200 11400 -64.242.88.10 - - [07/Mar/2004:17:13:50 -0800] "GET /twiki/bin/edit/TWiki/DefaultPlugin?t=1078688936 HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:17:16:00 -0800] "GET /twiki/bin/search/Main/?scope=topic®ex=on&search=^g HTTP/1.1" 200 3675 -64.242.88.10 - - [07/Mar/2004:17:17:27 -0800] "GET /twiki/bin/search/TWiki/?scope=topic®ex=on&search=^d HTTP/1.1" 200 5773 -lj1036.inktomisearch.com - - [07/Mar/2004:17:18:36 -0800] "GET /robots.txt HTTP/1.0" 200 68 -lj1090.inktomisearch.com - - [07/Mar/2004:17:18:41 -0800] "GET /twiki/bin/view/Main/LondonOffice HTTP/1.0" 200 3860 -64.242.88.10 - - [07/Mar/2004:17:21:44 -0800] "GET /twiki/bin/attach/TWiki/TablePlugin HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:17:22:49 -0800] "GET /twiki/bin/view/TWiki/ManagingWebs?rev=1.22 HTTP/1.1" 200 9310 -64.242.88.10 - - [07/Mar/2004:17:23:54 -0800] "GET /twiki/bin/statistics/Main HTTP/1.1" 200 808 -64.242.88.10 - - [07/Mar/2004:17:26:30 -0800] "GET /twiki/bin/view/TWiki/WikiCulture HTTP/1.1" 200 5935 -64.242.88.10 - - [07/Mar/2004:17:27:37 -0800] "GET /twiki/bin/edit/Main/WebSearch?t=1078669682 HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:17:28:45 -0800] "GET /twiki/bin/oops/TWiki/ResetPassword?template=oopsmore¶m1=1.4¶m2=1.4 HTTP/1.1" 200 11281 -64.242.88.10 - - [07/Mar/2004:17:29:59 -0800] "GET /twiki/bin/view/TWiki/ManagingWebs?skin=print HTTP/1.1" 200 8806 -64.242.88.10 - - [07/Mar/2004:17:31:39 -0800] "GET /twiki/bin/edit/Main/UvscanAndPostFix?topicparent=Main.WebHome HTTP/1.1" 401 12846 -64.242.88.10 - - [07/Mar/2004:17:35:35 -0800] "GET /twiki/bin/view/TWiki/KlausWriessnegger HTTP/1.1" 200 3848 -64.242.88.10 - - [07/Mar/2004:17:39:39 -0800] "GET /twiki/bin/view/Main/SpamAssassin HTTP/1.1" 200 4081 -64.242.88.10 - - [07/Mar/2004:17:42:15 -0800] "GET /twiki/bin/oops/TWiki/RichardDonkin?template=oopsmore¶m1=1.2¶m2=1.2 HTTP/1.1" 200 11281 -64.242.88.10 - - [07/Mar/2004:17:46:17 -0800] "GET /twiki/bin/rdiff/TWiki/AlWilliams?rev1=1.3&rev2=1.2 HTTP/1.1" 200 4485 -64.242.88.10 - - [07/Mar/2004:17:47:43 -0800] "GET /twiki/bin/rdiff/TWiki/AlWilliams?rev1=1.2&rev2=1.1 HTTP/1.1" 200 5234 -64.242.88.10 - - [07/Mar/2004:17:50:44 -0800] "GET /twiki/bin/view/TWiki/SvenDowideit HTTP/1.1" 200 3616 -64.242.88.10 - - [07/Mar/2004:17:53:45 -0800] "GET /twiki/bin/search/Main/SearchResult?scope=text®ex=on&search=Office%20*Locations[^A-Za-z] HTTP/1.1" 200 7771 diff --git a/src/Unit-1/FSharp/lesson4/Images/actor_path.png b/src/Unit-1/FSharp/lesson4/Images/actor_path.png deleted file mode 100644 index 808e02486..000000000 Binary files a/src/Unit-1/FSharp/lesson4/Images/actor_path.png and /dev/null differ diff --git a/src/Unit-1/FSharp/lesson4/Images/error_kernel.png b/src/Unit-1/FSharp/lesson4/Images/error_kernel.png deleted file mode 100644 index 859c8f48e..000000000 Binary files a/src/Unit-1/FSharp/lesson4/Images/error_kernel.png and /dev/null differ diff --git a/src/Unit-1/FSharp/lesson4/Images/guardians.png b/src/Unit-1/FSharp/lesson4/Images/guardians.png deleted file mode 100644 index 70ecf7bbb..000000000 Binary files a/src/Unit-1/FSharp/lesson4/Images/guardians.png and /dev/null differ diff --git a/src/Unit-1/FSharp/lesson4/Images/hierarchy_overview.png b/src/Unit-1/FSharp/lesson4/Images/hierarchy_overview.png deleted file mode 100644 index 68917d3f2..000000000 Binary files a/src/Unit-1/FSharp/lesson4/Images/hierarchy_overview.png and /dev/null differ diff --git a/src/Unit-1/FSharp/lesson4/Images/user_actors.png b/src/Unit-1/FSharp/lesson4/Images/user_actors.png deleted file mode 100644 index 603aa3916..000000000 Binary files a/src/Unit-1/FSharp/lesson4/Images/user_actors.png and /dev/null differ diff --git a/src/Unit-1/FSharp/lesson4/Images/working_tail_1.png b/src/Unit-1/FSharp/lesson4/Images/working_tail_1.png deleted file mode 100644 index ffcd9b06e..000000000 Binary files a/src/Unit-1/FSharp/lesson4/Images/working_tail_1.png and /dev/null differ diff --git a/src/Unit-1/FSharp/lesson4/Images/working_tail_2.png b/src/Unit-1/FSharp/lesson4/Images/working_tail_2.png deleted file mode 100644 index 5d9864242..000000000 Binary files a/src/Unit-1/FSharp/lesson4/Images/working_tail_2.png and /dev/null differ diff --git a/src/Unit-1/FSharp/lesson5/Images/selection_working.png b/src/Unit-1/FSharp/lesson5/Images/selection_working.png deleted file mode 100644 index 724b66afe..000000000 Binary files a/src/Unit-1/FSharp/lesson5/Images/selection_working.png and /dev/null differ diff --git a/src/Unit-1/FSharp/lesson6/Images/lifecycle.png b/src/Unit-1/FSharp/lesson6/Images/lifecycle.png deleted file mode 100644 index 0ff5b9582..000000000 Binary files a/src/Unit-1/FSharp/lesson6/Images/lifecycle.png and /dev/null differ diff --git a/src/Unit-1/FSharp/README.md b/src/Unit-1/README.md similarity index 96% rename from src/Unit-1/FSharp/README.md rename to src/Unit-1/README.md index 8dbeb9017..0aa773b0a 100644 --- a/src/Unit-1/FSharp/README.md +++ b/src/Unit-1/README.md @@ -1,6 +1,6 @@ # Akka.NET Bootcamp - Unit 1: Beginning Akka.NET -![Akka.NET logo](../../../images/akka_net_logo.png) +![Akka.NET logo](../../images/akka_net_logo.png) In Unit 1, we will learn the fundamentals of how the actor model and Akka.NET work. diff --git a/src/Unit-1/FSharp/lesson1/Completed/Actors.fs b/src/Unit-1/lesson1/Completed/Actors.fs similarity index 100% rename from src/Unit-1/FSharp/lesson1/Completed/Actors.fs rename to src/Unit-1/lesson1/Completed/Actors.fs diff --git a/src/Unit-1/FSharp/lesson1/Completed/App.config b/src/Unit-1/lesson1/Completed/App.config similarity index 100% rename from src/Unit-1/FSharp/lesson1/Completed/App.config rename to src/Unit-1/lesson1/Completed/App.config diff --git a/src/Unit-1/FSharp/lesson1/Completed/Program.fs b/src/Unit-1/lesson1/Completed/Program.fs similarity index 100% rename from src/Unit-1/FSharp/lesson1/Completed/Program.fs rename to src/Unit-1/lesson1/Completed/Program.fs diff --git a/src/Unit-1/FSharp/lesson1/Completed/WinTail.fsproj b/src/Unit-1/lesson1/Completed/WinTail.fsproj similarity index 100% rename from src/Unit-1/FSharp/lesson1/Completed/WinTail.fsproj rename to src/Unit-1/lesson1/Completed/WinTail.fsproj diff --git a/src/Unit-1/FSharp/lesson1/Completed/WinTail.sln b/src/Unit-1/lesson1/Completed/WinTail.sln similarity index 100% rename from src/Unit-1/FSharp/lesson1/Completed/WinTail.sln rename to src/Unit-1/lesson1/Completed/WinTail.sln diff --git a/src/Unit-1/FSharp/lesson1/Completed/packages.config b/src/Unit-1/lesson1/Completed/packages.config similarity index 100% rename from src/Unit-1/FSharp/lesson1/Completed/packages.config rename to src/Unit-1/lesson1/Completed/packages.config diff --git a/src/Unit-1/CSharp/lesson1/Images/Thumbs.db b/src/Unit-1/lesson1/Images/Thumbs.db similarity index 100% rename from src/Unit-1/CSharp/lesson1/Images/Thumbs.db rename to src/Unit-1/lesson1/Images/Thumbs.db diff --git a/src/Unit-1/CSharp/lesson1/Images/example.png b/src/Unit-1/lesson1/Images/example.png similarity index 100% rename from src/Unit-1/CSharp/lesson1/Images/example.png rename to src/Unit-1/lesson1/Images/example.png diff --git a/src/Unit-1/FSharp/lesson1/README.md b/src/Unit-1/lesson1/README.md similarity index 89% rename from src/Unit-1/FSharp/lesson1/README.md rename to src/Unit-1/lesson1/README.md index 598b21618..d0d3cb9db 100644 --- a/src/Unit-1/FSharp/lesson1/README.md +++ b/src/Unit-1/lesson1/README.md @@ -26,23 +26,23 @@ Because the actor system can be distributed over multiple machines in a cluster, We can think of an actor as a function that processes a message. Such a function would have a signature like -``` +```fsharp // 'M is the type of the message 'M -> unit ``` An actor can also be a function that sends a message to another actor. Such a function would have the signature -``` +```fsharp // 'M is the type of the message Actor<'M> -> 'M -> unit ``` And that's actually all we care about when we write our application - all the rest is plumbing... -Akka.FSharp gives us a couple of helpers to take these functions and inject them into an Actor System so that we can start interacting with them. We'll talk about how to create the actor system in a bit - you just need to be aware of the ``spawn``, ``actorOf`` and ``actorOf2`` functions provided by Akka.FSharp. +Akka.FSharp gives us a couple of helpers to take these functions and inject them into an Actor System so that we can start interacting with them. We'll talk about how to create the actor system in a bit - you just need to be aware of the `spawn`, `actorOf` and `actorOf2` functions provided by Akka.FSharp. -``` +```fsharp // for the message-processor kind of actor spawn actor_system "name" (actorOf (fn : 'M -> unit)) @@ -56,9 +56,9 @@ Actors communicate by sending messages to each other. Just like everything in F# The other really important thing to remember is that all these functions communicate asynchronously. There's a bunch of messaging logic under the hood that hides all of that from you, so basically you just write code that *looks* like you're calling a function, but you're actually doing a lot more! -For the moment, just think of the `` unit ``` -This function can be consumed by ``actorOf`` directly in order to ``spawn`` an actor. +This function can be consumed by `actorOf` directly in order to `spawn` an actor. The more complex function is one that is capable of sending a message to an actor -``` +```fsharp let consoleReaderActor (consoleWriter: IActorRef) (mailbox: Actor<_>) message = ... // this has the signature of : IActorRef -> Actor<'M> -> 'M -> unit ``` -So we can bind the consoleWriter actor to the first parameter and get ourselves a function which can be consumed by ``actorOf2``. +So we can bind the consoleWriter actor to the first parameter and get ourselves a function which can be consumed by `actorOf2`. ### Have ConsoleReaderActor Send a Message to ConsoleWriterActor Time to give your actors instructions! @@ -132,20 +122,20 @@ You will need to do the following in `Actors.fs`: 1. Have ConsoleReaderActor send a message to ConsoleWriterActor containing the content that it just read from the console. - ```fsharp - consoleWriter NOTE: `ActorSelection`s can be either absolute or relative. An absolute `ActorSelection` includes the root `/user/` actor in the path. However, an `ActorSelection` could also be relative, such as `select "../validationActor" mailbox.Context.System`. #### Doing elastic processing of large data streams -A very common extension of the well-known actor pattern is talking to routers ([docs](http://getakka.net/docs/Routing)). Routers are a more advanced topic we go in-depth on in unit 2. +A very common extension of the well-known actor pattern is talking to routers ([docs](http://getakka.net/articles/actors/routers.html)). Routers are a more advanced topic we go in-depth on in unit 2. As a quick example, imagine that you had a system that needed to process a large data stream in real time. Let's say for fun that you had a very popular consumer app that had peak usage once or twice a day, like a social app that people love to use on their breaks at work. Each user is generating a flurry of activity that has to be processed and rolled up in realtime. For the sake of this example, imagine that you had an actor running per user tracking the user's activity, and feeding that data to other parts of the system. diff --git a/src/Unit-1/FSharp/lesson6/Completed/Actors.fs b/src/Unit-1/lesson6/Completed/Actors.fs similarity index 100% rename from src/Unit-1/FSharp/lesson6/Completed/Actors.fs rename to src/Unit-1/lesson6/Completed/Actors.fs diff --git a/src/Unit-1/FSharp/lesson6/Completed/App.config b/src/Unit-1/lesson6/Completed/App.config similarity index 100% rename from src/Unit-1/FSharp/lesson6/Completed/App.config rename to src/Unit-1/lesson6/Completed/App.config diff --git a/src/Unit-1/FSharp/lesson6/Completed/FileObserver.fs b/src/Unit-1/lesson6/Completed/FileObserver.fs similarity index 100% rename from src/Unit-1/FSharp/lesson6/Completed/FileObserver.fs rename to src/Unit-1/lesson6/Completed/FileObserver.fs diff --git a/src/Unit-1/FSharp/lesson6/Completed/Messages.fs b/src/Unit-1/lesson6/Completed/Messages.fs similarity index 100% rename from src/Unit-1/FSharp/lesson6/Completed/Messages.fs rename to src/Unit-1/lesson6/Completed/Messages.fs diff --git a/src/Unit-1/FSharp/lesson6/Completed/Program.fs b/src/Unit-1/lesson6/Completed/Program.fs similarity index 100% rename from src/Unit-1/FSharp/lesson6/Completed/Program.fs rename to src/Unit-1/lesson6/Completed/Program.fs diff --git a/src/Unit-1/FSharp/lesson6/Completed/WinTail.fsproj b/src/Unit-1/lesson6/Completed/WinTail.fsproj similarity index 100% rename from src/Unit-1/FSharp/lesson6/Completed/WinTail.fsproj rename to src/Unit-1/lesson6/Completed/WinTail.fsproj diff --git a/src/Unit-1/FSharp/lesson6/Completed/WinTail.sln b/src/Unit-1/lesson6/Completed/WinTail.sln similarity index 100% rename from src/Unit-1/FSharp/lesson6/Completed/WinTail.sln rename to src/Unit-1/lesson6/Completed/WinTail.sln diff --git a/src/Unit-1/FSharp/lesson6/Completed/packages.config b/src/Unit-1/lesson6/Completed/packages.config similarity index 100% rename from src/Unit-1/FSharp/lesson6/Completed/packages.config rename to src/Unit-1/lesson6/Completed/packages.config diff --git a/src/Unit-1/CSharp/lesson6/Images/lifecycle.png b/src/Unit-1/lesson6/Images/lifecycle.png similarity index 100% rename from src/Unit-1/CSharp/lesson6/Images/lifecycle.png rename to src/Unit-1/lesson6/Images/lifecycle.png diff --git a/src/Unit-1/FSharp/lesson6/Images/lifecycle_methods.png b/src/Unit-1/lesson6/Images/lifecycle_methods.png similarity index 100% rename from src/Unit-1/FSharp/lesson6/Images/lifecycle_methods.png rename to src/Unit-1/lesson6/Images/lifecycle_methods.png diff --git a/src/Unit-1/FSharp/lesson6/README.md b/src/Unit-1/lesson6/README.md similarity index 98% rename from src/Unit-1/FSharp/lesson6/README.md rename to src/Unit-1/lesson6/README.md index baf9b0045..d36585d6f 100644 --- a/src/Unit-1/FSharp/lesson6/README.md +++ b/src/Unit-1/lesson6/README.md @@ -131,7 +131,7 @@ Compare your code to the solution in the [Completed](Completed/) folder to see w ## Great job! ### WOW! YOU WIN! Phenomenal work finishing Unit 1. -**Ready for more? [Start Unit 2 now](../../../Unit-2 "Akka.NET Bootcamp Unit 2").** +**Ready for more? [Start Unit 2 now](../../Unit-2 "Akka.NET Bootcamp Unit 2").** ## Any questions? **Don't be afraid to ask questions** :). diff --git a/src/Unit-2/README.md b/src/Unit-2/README.md index ffabb2bb8..f24e1618f 100644 --- a/src/Unit-2/README.md +++ b/src/Unit-2/README.md @@ -2,7 +2,7 @@ ![Akka.NET logo](../../images/akka_net_logo.png) -In **[Unit 1](../Unit-1/FSharp)**, we learned some of the fundamentals of Akka.NET and the actor model. +In **[Unit 1](../Unit-1)**, we learned some of the fundamentals of Akka.NET and the actor model. In Unit 2 we will learn some of the more sophisticated concepts behind Akka.NET, such as pattern matching, basic Akka.NET configuration, scheduled messages, and more! @@ -18,8 +18,8 @@ In fact, here's what the final output from lesson 4 looks like: In Unit 2 you will learn: -1. How to use [HOCON configuration](http://getakka.net/wiki/Configuration "Akka.NET HOCON Configurations") to configure your actors via App.config and Web.config; -2. How to configure your actor's [Dispatcher](http://getakka.net/wiki/Dispatchers) to run on the Windows Forms UI thread, so actors can make operations directly on UI elements without needing to change contexts; +1. How to use [HOCON configuration](http://getakka.net/articles/concepts/configuration.html "Akka.NET HOCON Configurations") to configure your actors via App.config and Web.config; +2. How to configure your actor's [Dispatcher](http://getakka.net/articles/actors/dispatchers.html) to run on the Windows Forms UI thread, so actors can make operations directly on UI elements without needing to change contexts; 3. How to use the `Scheduler` to send recurring messages to actors; 4. How to use the [Publish-subscribe (pub-sub) pattern](http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) between actors; 5. How and why to switch actor's behavior at run-time; and diff --git a/src/Unit-2/lesson1/README.md b/src/Unit-2/lesson1/README.md index b00124463..e5ada06f9 100644 --- a/src/Unit-2/lesson1/README.md +++ b/src/Unit-2/lesson1/README.md @@ -22,7 +22,7 @@ Does this mean we have to rewrite `chartingActor` with some evil code to manuall Nope! We can relax. -**We can solve this problem using [HOCON configuration in Akka.NET](http://getakka.net/docs/concepts/configuration) without updating any of the code that defines `chartingActor`.** +**We can solve this problem using [HOCON configuration in Akka.NET](http://getakka.net/articles/concepts/configuration.html) without updating any of the code that defines `chartingActor`.** But first, we need to understand `Dispatcher`s. @@ -51,7 +51,7 @@ The `SynchronizedDispatcher` uses the *current* [SynchronizationContext](https:/ In this lesson, we're going to use the `SynchronizedDispatcher` to ensure that the `chartingActor` runs on the UI thread of our WinForms application. That way, the `chartingActor` can update any UI element it wants without having to do any cross-thread marshalling - the actor's `Dispatcher` can automatically take care of that for us! -##### [`ForkJoinDispatcher`](http://api.getakka.net/docs/stable/html/F0DC1571.htm "Akka.NET Stable API Docs - ForkJoinDispatcher") +##### [`ForkJoinDispatcher`](http://getakka.net/articles/actors/dispatchers.html#forkjoindispatcher "Akka.NET Stable API Docs - ForkJoinDispatcher") This `Dispatcher` runs actors on top of a dedicated group of threads, for tunable concurrency. This is meant for actors that need their own dedicated threads in order to run (that need isolation guarantees). This is primarily used by `System` actors so you won't touch it much. @@ -78,7 +78,7 @@ Time to meet HOCON. Akka.NET leverages a configuration format, called HOCON, to allow you to configure your Akka.NET applications with whatever level of granularity you want. #### What is HOCON? -[HOCON (Human-Optimized Config Object Notation)](http://getakka.net/docs/concepts/hocon) is a flexible and extensible configuration format. It will allow you to configure everything from Akka.NET's `IActorRefProvider` implementation, logging, network transports, and more commonly - how individual actors are deployed. +[HOCON (Human-Optimized Config Object Notation)](http://getakka.net/articles/concepts/configuration.html#what-is-hocon) is a flexible and extensible configuration format. It will allow you to configure everything from Akka.NET's `IActorRefProvider` implementation, logging, network transports, and more commonly - how individual actors are deployed. Values returned by HOCON are strongly typed (i.e. you can fetch out an `int`, a `Timespan`, etc). @@ -292,7 +292,7 @@ Nice work on completing your first lesson in Unit 2! We covered a lot of concept **Let's move onto [Lesson 2 - Using ReceiveActor for Smarter Message Handling](../lesson2).** ## Further reading -As you probably guessed while reading the HOCON configs above, any line with `#` at the front of it is treated as a comment in HOCON. [Learn more about HOCON syntax here](http://getakka.net/docs/HOCON). +As you probably guessed while reading the HOCON configs above, any line with `#` at the front of it is treated as a comment in HOCON. [Learn more about HOCON syntax here](http://getakka.net/articles/concepts/configuration.html#hocon-can-be-used-inside-appconfig-and-webconfig). ## Any questions? **Don't be afraid to ask questions** :). diff --git a/src/Unit-2/lesson2/README.md b/src/Unit-2/lesson2/README.md index 50c3df567..a7f2d9bb4 100644 --- a/src/Unit-2/lesson2/README.md +++ b/src/Unit-2/lesson2/README.md @@ -1,6 +1,6 @@ # Lesson 2.2: Using `ReceiveActor` for Smarter Message Handling -In this lesson we're going to introduce the concept of `ReceiveActor` ([docs](http://api.getakka.net/docs/stable/html/B124B2AF.htm "Akka.NET - ReceiveActor")). +In this lesson we're going to introduce the concept of `ReceiveActor` ([docs](http://getakka.net/api/Akka.Actor.ReceiveActor.html "Akka.NET - ReceiveActor")). This concept is mainly used in the **C#** API to deal more easily with sophisticated types of pattern matching and message handling in Akka.NET. In F# however, the concept of `ReceiveActor` is not as important, pattern matching already being an inherent part of the language. @@ -185,7 +185,7 @@ In this case is that the second handler associated with `str.StartsWith "AkkaDot ***The order in which `Receive<'T>` handlers are declared matters!*** -This is because **`ReceiveActor` will handle a message using the *first* matching handler, not the *best* matching handler** and it [evaluates its handlers for each message in the order in which they were declared](http://getakka.net/docs/ReceiveActor#handler-priority). +This is because **`ReceiveActor` will handle a message using the *first* matching handler, not the *best* matching handler** and it [evaluates its handlers for each message in the order in which they were declared](http://getakka.net/articles/actors/receive-actor-api.html#handler-priority). So, how to solve our problem above and make sure our "AkkaDotNetSuccess" handler is being triggered? diff --git a/src/Unit-2/lesson3/README.md b/src/Unit-2/lesson3/README.md index 5ba2a5042..b09da7a4c 100644 --- a/src/Unit-2/lesson3/README.md +++ b/src/Unit-2/lesson3/README.md @@ -22,7 +22,7 @@ Perhaps you want an actor to periodically fetch information, or to occasionally Akka.NET provides a mechanism for doing just this sort of thing. Meet your new best friend: the `Scheduler`. ### What is the `Scheduler`? -The `ActorSystem.Scheduler` ([docs](http://api.getakka.net/docs/stable/html/FB15E2E6.htm "Akka.NET Stable API Docs - IScheduler interface")) is a singleton within every `ActorSystem` that allows you to schedule messages to be sent to an actor in the future. The `Scheduler` can send both one-off and recurring messages. +The `ActorSystem.Scheduler` ([docs](http://getakka.net/api/Akka.Actor.IScheduler.html "Akka.NET Stable API Docs - IScheduler interface")) is a singleton within every `ActorSystem` that allows you to schedule messages to be sent to an actor in the future. The `Scheduler` can send both one-off and recurring messages. ### How do I use the `Scheduler`? As we mentioned, you can schedule one-off or recurring messages to an actor. @@ -42,7 +42,7 @@ mailbox.Context.System.Scheduler.ScheduleTellOnce (TimeSpan.FromMinutes 30., som ``` #### Schedule one-off messages with `ScheduleTellOnce()` -Let's say we want to have one of our actors fetch the latest content from an RSS feed 30 minutes in the future. We can use [`IScheduler.ScheduleTellOnce()`](http://api.getakka.net/docs/stable/html/190E4EB.htm "Akka.NET Stable API Docs - IScheduler.ScheduleTellOnce method") to do that: +Let's say we want to have one of our actors fetch the latest content from an RSS feed 30 minutes in the future. We can use [`IScheduler.ScheduleTellOnce()`](http://getakka.net/api/Akka.Actor.ITellScheduler.html#Akka_Actor_ITellScheduler_ScheduleTellOnce_System_TimeSpan_Akka_Actor_ICanTell_System_Object_Akka_Actor_IActorRef_ "Akka.NET Stable API Docs - IScheduler.ScheduleTellOnce method") to do that: ```fsharp let actorSystem = System.create "myActorSystem" (Configuration.load ()) @@ -58,7 +58,7 @@ Voila! `someActor` will receive `someMessage` in 30 minutes time. #### Schedule recurring messages with `ScheduleTellRepeatedly()` Now, **what if we want to schedule this message to be delivered once *every 30 minutes*?** -For this we can use the following [`IScheduler.ScheduleTellRepeatedly()`](http://api.getakka.net/docs/stable/html/A909C289.htm "Akka.NET Stable API Docs - IScheduler.ScheduleTellRepeatedly") overload. +For this we can use the following [`IScheduler.ScheduleTellRepeatedly()`](http://getakka.net/api/Akka.Actor.ITellScheduler.html#Akka_Actor_ITellScheduler_ScheduleTellRepeatedly_System_TimeSpan_System_TimeSpan_Akka_Actor_ICanTell_System_Object_Akka_Actor_IActorRef_ "Akka.NET Stable API Docs - IScheduler.ScheduleTellRepeatedly") overload. ```fsharp let actorSystem = System.create "myActorSystem" (Configuration.load ()) @@ -76,7 +76,7 @@ actorSystem.Scheduler.ScheduleTellRepeatedly( That's it! ### How do I cancel a scheduled message? -What happens if we need to cancel a scheduled or recurring message? We use a [`ICancelable`](http://api.getakka.net/docs/stable/html/3FA8058E.htm "Akka.NET Stable API Docs - ICancelable interface"), which we can create using a [`Cancelable`](http://api.getakka.net/docs/stable/html/8869EC52.htm) instance. +What happens if we need to cancel a scheduled or recurring message? We use a [`ICancelable`](http://getakka.net/api/Akka.Actor.ICancelable.html "Akka.NET Stable API Docs - ICancelable interface"), which we can create using a [`Cancelable`](http://getakka.net/api/Akka.Actor.Cancelable.html) instance. First, the message must be scheduled so that it can be cancelled. If a message is cancelable, we then just have to call `Cancel()` on our handle to the `ICancelable` and it will not be delivered. For example: @@ -100,7 +100,7 @@ cancellation.Cancel () ``` #### Alternative: get an `ICancelable` task using `ScheduleTellRepeatedlyCancelable` -One of the new `IScheduler` methods we introduced in Akka.NET v1.0 is the [`ScheduleTellRepeatedlyCancelable` extension method](http://api.getakka.net/docs/stable/html/9B66375D.htm "Akka.NET API Docs - SchedulerExtensions.ScheduleTellRepeatedlyCancelable extension method")]. This extension method inlines the process of creating an `ICancelable` instance for your recurring messages and simply returns an `ICancelable` for you. +One of the new `IScheduler` methods we introduced in Akka.NET v1.0 is the [`ScheduleTellRepeatedlyCancelable` extension method](http://getakka.net/api/Akka.Actor.SchedulerExtensions.html#Akka_Actor_SchedulerExtensions_ScheduleTellRepeatedlyCancelable_Akka_Actor_IScheduler_System_TimeSpan_System_TimeSpan_Akka_Actor_ICanTell_System_Object_Akka_Actor_IActorRef_ "Akka.NET API Docs - SchedulerExtensions.ScheduleTellRepeatedlyCancelable extension method")]. This extension method inlines the process of creating an `ICancelable` instance for your recurring messages and simply returns an `ICancelable` for you. ```fsharp let actorSystem = System.create "myActorSystem" (Configuration.load ()) @@ -135,12 +135,12 @@ Here are all the overload options you have for scheduling a message. #### Overloads of `ScheduleTellRepeatedly` These are the various API calls you can make to schedule recurring messages. -[Refer to the `IScheduler` API documentation](http://api.getakka.net/docs/stable/html/FB15E2E6.htm "Akka.NET Stable API Documentation - IScheduler Interface"). +[Refer to the `IScheduler` API documentation](http://getakka.net/api/Akka.Actor.IScheduler.html "Akka.NET Stable API Documentation - IScheduler Interface"). #### Overloads of `ScheduleTellOnce` These are the various API calls you can make to schedule one-off messages. -[Refer to the `IScheduler` API documentation](http://api.getakka.net/docs/stable/html/FB15E2E6.htm "Akka.NET Stable API Documentation - IScheduler Interface"). +[Refer to the `IScheduler` API documentation](http://getakka.net/api/Akka.Actor.IScheduler.html "Akka.NET Stable API Documentation - IScheduler Interface"). ### How do I do Pub/Sub with Akka.NET Actors? It's actually very simple. Many people expect this to be very complicated and are suspicious that there isn't more code involved. Rest assured, there's nothing magic about pub/sub with Akka.NET actors. It can literally be as simple as this: diff --git a/src/Unit-2/lesson4/README.md b/src/Unit-2/lesson4/README.md index 8849ec6e8..c16bf90fa 100644 --- a/src/Unit-2/lesson4/README.md +++ b/src/Unit-2/lesson4/README.md @@ -1,6 +1,6 @@ # Lesson 2.4: Switching Actor Behavior at Run-time -In this lesson we're going to learn about one of the really cool things actors can do: [change their behavior at run-time](http://getakka.net/docs/Working%20with%20actors#hotswap "Akka.NET - Actor behavior hotswap")! +In this lesson we're going to learn about one of the really cool things actors can do: [change their behavior at run-time](http://getakka.net/articles/actors/receive-actor-api.html#becomeunbecome "Akka.NET - Actor behavior hotswap")! ## Key Concepts / Background Let's start with a real-world scenario in which you'd want the ability to change an actor's behavior. diff --git a/src/Unit-2/lesson5/README.md b/src/Unit-2/lesson5/README.md index 7c7416870..f389f9b0c 100644 --- a/src/Unit-2/lesson5/README.md +++ b/src/Unit-2/lesson5/README.md @@ -13,7 +13,7 @@ So, how can we fix this? The answer is to defer processing of `AddSeries` and `RemoveSeries` messages until the `ChartingActor` is back in its `Charting` behavior, at which time it can actually do something with those messages. -The mechanism for this is the [`Stash`](http://getakka.net/docs/Stash). +The mechanism for this is the [`Stash`](http://getakka.net/articles/actors/receive-actor-api.html#stash). ## Key Concepts / Background One of the side effects of switchable behaviors for actors is that some behaviors may not be able to process specific types of messages. For instance, let's consider the authentication example we used for behavior-switching in [Lesson 4](../lesson4/). diff --git a/src/Unit-3/DoThis/ActorPaths.cs b/src/Unit-3/DoThis/ActorPaths.cs deleted file mode 100644 index ae491dd9b..000000000 --- a/src/Unit-3/DoThis/ActorPaths.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Akka.Actor; - -namespace GithubActors -{ - /// - /// Static helper class used to define paths to fixed-name actors - /// (helps eliminate errors when using ) - /// - public static class ActorPaths - { - public static readonly ActorMetaData GithubAuthenticatorActor = new ActorMetaData("authenticator", "akka://GithubActors/user/authenticator"); - public static readonly ActorMetaData MainFormActor = new ActorMetaData("mainform", "akka://GithubActors/user/mainform"); - public static readonly ActorMetaData GithubValidatorActor = new ActorMetaData("validator", "akka://GithubActors/user/validator"); - public static readonly ActorMetaData GithubCommanderActor = new ActorMetaData("commander", "akka://GithubActors/user/commander"); - public static readonly ActorMetaData GithubCoordinatorActor = new ActorMetaData("coordinator", "akka://GithubActors/user/commander/coordinator"); - } - - /// - /// Meta-data class - /// - public class ActorMetaData - { - public ActorMetaData(string name, string path) - { - Name = name; - Path = path; - } - - public string Name { get; private set; } - - public string Path { get; private set; } - } -} diff --git a/src/Unit-3/FSharpDoThis/ActorSystem.fs b/src/Unit-3/DoThis/ActorSystem.fs similarity index 100% rename from src/Unit-3/FSharpDoThis/ActorSystem.fs rename to src/Unit-3/DoThis/ActorSystem.fs diff --git a/src/Unit-3/FSharpDoThis/Actors.fs b/src/Unit-3/DoThis/Actors.fs similarity index 100% rename from src/Unit-3/FSharpDoThis/Actors.fs rename to src/Unit-3/DoThis/Actors.fs diff --git a/src/Unit-3/DoThis/Actors/GithubAuthenticationActor.cs b/src/Unit-3/DoThis/Actors/GithubAuthenticationActor.cs deleted file mode 100644 index 8c2523ce9..000000000 --- a/src/Unit-3/DoThis/Actors/GithubAuthenticationActor.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System.Drawing; -using Akka.Actor; -using Octokit; -using Label = System.Windows.Forms.Label; - -namespace GithubActors.Actors -{ - public class GithubAuthenticationActor : ReceiveActor - { - #region Messages - - public class Authenticate - { - public Authenticate(string oAuthToken) - { - OAuthToken = oAuthToken; - } - - public string OAuthToken { get; private set; } - } - - public class AuthenticationFailed { } - - public class AuthenticationCancelled { } - - public class AuthenticationSuccess { } - - #endregion - - private readonly Label _statusLabel; - private readonly GithubAuth _form; - - public GithubAuthenticationActor(Label statusLabel, GithubAuth form) - { - _statusLabel = statusLabel; - _form = form; - Unauthenticated(); - } - - private void Unauthenticated() - { - Receive(auth => - { - //need a client to test our credentials with - var client = GithubClientFactory.GetUnauthenticatedClient(); - GithubClientFactory.OAuthToken = auth.OAuthToken; - client.Credentials = new Credentials(auth.OAuthToken); - BecomeAuthenticating(); - client.User.Current().ContinueWith(tr => - { - if (tr.IsFaulted) - return new AuthenticationFailed(); - if (tr.IsCanceled) - return new AuthenticationCancelled(); - return new AuthenticationSuccess(); - }).PipeTo(Self); - }); - } - - private void BecomeAuthenticating() - { - _statusLabel.Visible = true; - _statusLabel.ForeColor = Color.Yellow; - _statusLabel.Text = "Authenticating..."; - Become(Authenticating); - } - - private void BecomeUnauthenticated(string reason) - { - _statusLabel.ForeColor = Color.Red; - _statusLabel.Text = "Authentication failed. Please try again."; - Become(Unauthenticated); - } - - private void Authenticating() - { - Receive(failed => BecomeUnauthenticated("Authentication failed.")); - Receive(cancelled => BecomeUnauthenticated("Authentication timed out.")); - Receive(success => - { - var launcherForm = new LauncherForm(); - launcherForm.Show(); - _form.Hide(); - }); - } - } -} diff --git a/src/Unit-3/DoThis/Actors/GithubCommanderActor.cs b/src/Unit-3/DoThis/Actors/GithubCommanderActor.cs deleted file mode 100644 index 529c37f8e..000000000 --- a/src/Unit-3/DoThis/Actors/GithubCommanderActor.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using Akka.Actor; - -namespace GithubActors.Actors -{ - /// - /// Top-level actor responsible for coordinating and launching repo-processing jobs - /// - public class GithubCommanderActor : ReceiveActor - { - #region Message classes - - public class CanAcceptJob - { - public CanAcceptJob(RepoKey repo) - { - Repo = repo; - } - - public RepoKey Repo { get; private set; } - } - - public class AbleToAcceptJob - { - public AbleToAcceptJob(RepoKey repo) - { - Repo = repo; - } - - public RepoKey Repo { get; private set; } - } - - public class UnableToAcceptJob - { - public UnableToAcceptJob(RepoKey repo) - { - Repo = repo; - } - - public RepoKey Repo { get; private set; } - } - - #endregion - - private ActorRef _coordinator; - private ActorRef _canAcceptJobSender; - - public GithubCommanderActor() - { - Receive(job => - { - _canAcceptJobSender = Sender; - _coordinator.Tell(job); - }); - - Receive(job => - { - _canAcceptJobSender.Tell(job); - }); - - Receive(job => - { - _canAcceptJobSender.Tell(job); - - //start processing messages - _coordinator.Tell(new GithubCoordinatorActor.BeginJob(job.Repo)); - - //launch the new window to view results of the processing - Context.ActorSelection(ActorPaths.MainFormActor.Path).Tell(new MainFormActor.LaunchRepoResultsWindow(job.Repo, Sender)); - }); - } - - protected override void PreStart() - { - _coordinator = Context.ActorOf(Props.Create(() => new GithubCoordinatorActor()), ActorPaths.GithubCoordinatorActor.Name); - base.PreStart(); - } - - protected override void PreRestart(Exception reason, object message) - { - //kill off the old coordinator so we can recreate it from scratch - _coordinator.Tell(PoisonPill.Instance); - base.PreRestart(reason, message); - } - } -} diff --git a/src/Unit-3/DoThis/Actors/GithubCoordinatorActor.cs b/src/Unit-3/DoThis/Actors/GithubCoordinatorActor.cs deleted file mode 100644 index 68254d3a1..000000000 --- a/src/Unit-3/DoThis/Actors/GithubCoordinatorActor.cs +++ /dev/null @@ -1,198 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using Akka.Actor; -using Octokit; - -namespace GithubActors.Actors -{ - /// - /// Actor responsible for publishing data about the results - /// of a github operation - /// - public class GithubCoordinatorActor : ReceiveActor - { - #region Message classes - - public class BeginJob - { - public BeginJob(RepoKey repo) - { - Repo = repo; - } - - public RepoKey Repo { get; private set; } - } - - public class SubscribeToProgressUpdates - { - public SubscribeToProgressUpdates(ActorRef subscriber) - { - Subscriber = subscriber; - } - - public ActorRef Subscriber { get; private set; } - } - - public class PublishUpdate - { - private PublishUpdate() { } - private static readonly PublishUpdate _instance = new PublishUpdate(); - - public static PublishUpdate Instance - { - get { return _instance; } - } - } - - /// - /// Let the subscribers know we failed - /// - public class JobFailed - { - public JobFailed(RepoKey repo) - { - Repo = repo; - } - - public RepoKey Repo { get; private set; } - } - - #endregion - - private ActorRef _githubWorker; - - private RepoKey _currentRepo; - private Dictionary _similarRepos; - private HashSet _subscribers; - private CancellationTokenSource _publishTimer; - private GithubProgressStats _githubProgressStats; - - private bool _receivedInitialUsers = false; - - public GithubCoordinatorActor() - { - Waiting(); - } - - protected override void PreStart() - { - _githubWorker = Context.ActorOf(Props.Create(() => new GithubWorkerActor(GithubClientFactory.GetClient))); - } - - private void Waiting() - { - Receive(job => Sender.Tell(new GithubCommanderActor.AbleToAcceptJob(job.Repo))); - Receive(job => - { - BecomeWorking(job.Repo); - - //kick off the job to query the repo's list of starrers - _githubWorker.Tell(new RetryableQuery(new GithubWorkerActor.QueryStarrers(job.Repo), 4)); - }); - } - - private void BecomeWorking(RepoKey repo) - { - _receivedInitialUsers = false; - _currentRepo = repo; - _subscribers = new HashSet(); - _similarRepos = new Dictionary(); - _publishTimer = new CancellationTokenSource(); - _githubProgressStats = new GithubProgressStats(); - Become(Working); - } - - private void BecomeWaiting() - { - //stop publishing - _publishTimer.Cancel(); - Become(Waiting); - } - - private void Working() - { - //received a downloaded user back from the github worker - Receive(user => - { - _githubProgressStats = _githubProgressStats.UserQueriesFinished(); - foreach (var repo in user.Repos) - { - if (!_similarRepos.ContainsKey(repo.HtmlUrl)) - { - _similarRepos[repo.HtmlUrl] = new SimilarRepo(repo); - } - - //increment the number of people who starred this repo - _similarRepos[repo.HtmlUrl].SharedStarrers++; - } - }); - - Receive(update => - { - //check to see if the job is done - if (_receivedInitialUsers && _githubProgressStats.IsFinished) - { - _githubProgressStats = _githubProgressStats.Finish(); - - //all repos minus forks of the current one - var sortedSimilarRepos = _similarRepos.Values - .Where(x => x.Repo.Name != _currentRepo.Repo).OrderByDescending(x => x.SharedStarrers).ToList(); - foreach (var subscriber in _subscribers) - { - subscriber.Tell(sortedSimilarRepos); - } - BecomeWaiting(); - } - - foreach (var subscriber in _subscribers) - { - subscriber.Tell(_githubProgressStats); - } - }); - - //completed our initial job - we now know how many users we need to query - Receive(users => - { - _receivedInitialUsers = true; - _githubProgressStats = _githubProgressStats.SetExpectedUserCount(users.Length); - - //queue up all of the jobs - foreach (var user in users) - _githubWorker.Tell(new RetryableQuery(new GithubWorkerActor.QueryStarrer(user.Login), 3)); - }); - - Receive(job => Sender.Tell(new GithubCommanderActor.UnableToAcceptJob(job.Repo))); - - Receive(updates => - { - //this is our first subscriber, which means we need to turn publishing on - if (_subscribers.Count == 0) - { - Context.System.Scheduler.Schedule(TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(100), - Self, PublishUpdate.Instance, _publishTimer.Token); - } - - _subscribers.Add(updates.Subscriber); - }); - - //query failed, but can be retried - Receive(query => query.CanRetry, query => _githubWorker.Tell(query)); - - //query failed, can't be retried, and it's a QueryStarrers operation - means the entire job failed - Receive(query => !query.CanRetry && query.Query is GithubWorkerActor.QueryStarrers, query => - { - _receivedInitialUsers = true; - foreach (var subscriber in _subscribers) - { - subscriber.Tell(new JobFailed(_currentRepo)); - } - BecomeWaiting(); - }); - - //query failed, can't be retried, and it's a QueryStarrers operation - means individual operation failed - Receive(query => !query.CanRetry && query.Query is GithubWorkerActor.QueryStarrer, query => _githubProgressStats.IncrementFailures()); - } - } -} diff --git a/src/Unit-3/DoThis/Actors/GithubValidatorActor.cs b/src/Unit-3/DoThis/Actors/GithubValidatorActor.cs deleted file mode 100644 index ebd17e1fd..000000000 --- a/src/Unit-3/DoThis/Actors/GithubValidatorActor.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System; -using System.Linq; -using Akka.Actor; -using Octokit; - -namespace GithubActors.Actors -{ - /// - /// Actor has one job - ensure that a public repo exists at the specified address - /// - public class GithubValidatorActor : ReceiveActor - { - #region Messages - - public class ValidateRepo - { - public ValidateRepo(string repoUri) - { - RepoUri = repoUri; - } - - public string RepoUri { get; private set; } - } - - public class InvalidRepo - { - public InvalidRepo(string repoUri, string reason) - { - Reason = reason; - RepoUri = repoUri; - } - - public string RepoUri { get; private set; } - - public string Reason { get; private set; } - } - - /// - /// System is unable to process additional repos at this time - /// - public class SystemBusy { } - - /// - /// This is a valid repository - /// - public class RepoIsValid - { - /* - * Using singleton pattern here since it's a stateless message. - * - * Considered to be a good practice to eliminate unnecessary garbage collection, - * and it's used internally inside Akka.NET for similar scenarios. - */ - private RepoIsValid() { } - private static readonly RepoIsValid _instance = new RepoIsValid(); - public static RepoIsValid Instance { get { return _instance; } } - } - - #endregion - - private readonly IGitHubClient _gitHubClient; - - public GithubValidatorActor(IGitHubClient gitHubClient) - { - _gitHubClient = gitHubClient; - ReadyToValidate(); - } - - private void ReadyToValidate() - { - //Outright invalid URLs - Receive(repo => string.IsNullOrEmpty(repo.RepoUri) || !Uri.IsWellFormedUriString(repo.RepoUri, UriKind.Absolute), - repo => Sender.Tell(new InvalidRepo(repo.RepoUri, "Not a valid absolute URI"))); - - //Repos that at least have a valid absolute URL - Receive(repo => - { - var userOwner = SplitIntoOwnerAndRepo(repo.RepoUri); - //close over the sender in an instance variable - var sender = Sender; - _gitHubClient.Repository.Get(userOwner.Item1, userOwner.Item2).ContinueWith(t => - { - //Rule #1 of async in Akka.NET - turn exceptions into messages your actor understands - if (t.IsCanceled) - { - return new InvalidRepo(repo.RepoUri, "Repo lookup timed out"); - } - if (t.IsFaulted) - { - return new InvalidRepo(repo.RepoUri, t.Exception != null ? t.Exception.GetBaseException().Message : "Unknown Octokit error"); - } - - return t.Result; - }).PipeTo(Self, sender); - }); - - // something went wrong while querying github, sent to ourselves via PipeTo - // however - Sender gets preserved on the call, so it's safe to use Forward here. - Receive(repo => Sender.Forward(repo)); - - // Octokit was able to retrieve this repository - Receive(repository => - { - //ask the GithubCommander if we can accept this job - Context.ActorSelection(ActorPaths.GithubCommanderActor.Path).Tell(new GithubCommanderActor.CanAcceptJob(new RepoKey(repository.Owner.Login, repository.Name))); - }); - - - /* REPO is valid, but can we process it at this time? */ - - //yes - Receive(job => Context.ActorSelection(ActorPaths.MainFormActor.Path).Tell(job)); - - //no - Receive(job => Context.ActorSelection(ActorPaths.MainFormActor.Path).Tell(job)); - } - - public static Tuple SplitIntoOwnerAndRepo(string repoUri) - { - var split = new Uri(repoUri, UriKind.Absolute).PathAndQuery.TrimEnd('/').Split('/').Reverse().ToList(); //uri path without trailing slash - return Tuple.Create(split[1], split[0]); //User, Repo - } - } -} diff --git a/src/Unit-3/DoThis/Actors/GithubWorkerActor.cs b/src/Unit-3/DoThis/Actors/GithubWorkerActor.cs deleted file mode 100644 index 1b03f9978..000000000 --- a/src/Unit-3/DoThis/Actors/GithubWorkerActor.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Akka.Actor; -using Octokit; - -namespace GithubActors.Actors -{ - /// - /// Individual actor responsible for querying the Github API - /// - public class GithubWorkerActor : ReceiveActor - { - #region Message classes - - public class QueryStarrers - { - public QueryStarrers(RepoKey key) - { - Key = key; - } - - public RepoKey Key { get; private set; } - } - - /// - /// Query an individual starrer - /// - public class QueryStarrer - { - public QueryStarrer(string login) - { - Login = login; - } - - public string Login { get; private set; } - } - - public class StarredReposForUser - { - public StarredReposForUser(string login, IEnumerable repos) - { - Repos = repos; - Login = login; - } - - public string Login { get; private set; } - - public IEnumerable Repos { get; private set; } - } - - #endregion - - private IGitHubClient _gitHubClient; - private readonly Func _gitHubClientFactory; - - public GithubWorkerActor(Func gitHubClientFactory) - { - _gitHubClientFactory = gitHubClientFactory; - InitialReceives(); - } - - protected override void PreStart() - { - _gitHubClient = _gitHubClientFactory(); - } - - private void InitialReceives() - { - //query an individual starrer - Receive(query => query.Query is QueryStarrer, query => - { - // ReSharper disable once PossibleNullReferenceException (we know from the previous IS statement that this is not null) - var starrer = (query.Query as QueryStarrer).Login; - try - { - var getStarrer = _gitHubClient.Activity.Starring.GetAllForUser(starrer); - - //ewww - getStarrer.Wait(); - var starredRepos = getStarrer.Result; - Sender.Tell(new StarredReposForUser(starrer, starredRepos)); - } - catch (Exception ex) - { - //operation failed - let the parent know - Sender.Tell(query.NextTry()); - } - }); - - //query all starrers for a repository - Receive(query => query.Query is QueryStarrers, query => - { - // ReSharper disable once PossibleNullReferenceException (we know from the previous IS statement that this is not null) - var starrers = (query.Query as QueryStarrers).Key; - try - { - var getStars = _gitHubClient.Activity.Starring.GetAllStargazers(starrers.Owner, starrers.Repo); - - //ewww - getStars.Wait(); - var stars = getStars.Result; - Sender.Tell(stars.ToArray()); - } - catch (Exception ex) - { - //operation failed - let the parent know - Sender.Tell(query.NextTry()); - } - }); - } - } -} diff --git a/src/Unit-3/DoThis/Actors/MainFormActor.cs b/src/Unit-3/DoThis/Actors/MainFormActor.cs deleted file mode 100644 index ca337b2cc..000000000 --- a/src/Unit-3/DoThis/Actors/MainFormActor.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System.Drawing; -using System.Windows.Forms; -using Akka.Actor; - -namespace GithubActors.Actors -{ - /// - /// Actor that runs on the UI thread and handles - /// UI events for - /// - public class MainFormActor : ReceiveActor, WithUnboundedStash - { - #region Messages - - public class LaunchRepoResultsWindow - { - public LaunchRepoResultsWindow(RepoKey repo, ActorRef coordinator) - { - Repo = repo; - Coordinator = coordinator; - } - - public RepoKey Repo { get; private set; } - - public ActorRef Coordinator { get; private set; } - } - - #endregion - - private readonly Label _validationLabel; - - public MainFormActor(Label validationLabel) - { - _validationLabel = validationLabel; - Ready(); - } - - /// - /// State for when we're able to accept new jobs - /// - private void Ready() - { - Receive(repo => - { - Context.ActorSelection(ActorPaths.GithubValidatorActor.Path).Tell(new GithubValidatorActor.ValidateRepo(repo.RepoUri)); - BecomeBusy(repo.RepoUri); - }); - - //launch the window - Receive(window => - { - var form = new RepoResultsForm(window.Coordinator, window.Repo); - form.Show(); - }); - } - - /// - /// Make any necessary URI updates, then switch our state to busy - /// - private void BecomeBusy(string repoUrl) - { - _validationLabel.Visible = true; - _validationLabel.Text = string.Format("Validating {0}...", repoUrl); - _validationLabel.ForeColor = Color.Gold; - Become(Busy); - } - - /// - /// State for when we're currently processing a job - /// - private void Busy() - { - Receive(valid => BecomeReady("Valid!")); - Receive(invalid => BecomeReady(invalid.Reason, false)); - //yes - Receive(job => BecomeReady(string.Format("{0}/{1} is a valid repo, but system can't accept additional jobs", job.Repo.Owner, job.Repo.Repo), false)); - - //no - Receive(job => BecomeReady(string.Format("{0}/{1} is a valid repo - starting job!", job.Repo.Owner, job.Repo.Repo))); - Receive(window => Stash.Stash()); - } - - private void BecomeReady(string message, bool isValid = true) - { - _validationLabel.Text = message; - _validationLabel.ForeColor = isValid ? Color.Green : Color.Red; - Stash.UnstashAll(); - Become(Ready); - } - - public IStash Stash { get; set; } - } -} diff --git a/src/Unit-3/DoThis/Actors/RepoResultsActor.cs b/src/Unit-3/DoThis/Actors/RepoResultsActor.cs deleted file mode 100644 index ff49d5c51..000000000 --- a/src/Unit-3/DoThis/Actors/RepoResultsActor.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Collections.Generic; -using System.Drawing; -using System.Windows.Forms; -using Akka.Actor; - -namespace GithubActors.Actors -{ - /// - /// Actor responsible for printing the results and progress from a - /// onto a (runs on the UI thread) - /// - public class RepoResultsActor : ReceiveActor - { - private DataGridView _userDg; - private ToolStripStatusLabel _statusLabel; - private ToolStripProgressBar _progressBar; - - private bool _hasSetProgress = false; - - public RepoResultsActor(DataGridView userDg, ToolStripStatusLabel statusLabel, ToolStripProgressBar progressBar) - { - _userDg = userDg; - _statusLabel = statusLabel; - _progressBar = progressBar; - InitialReceives(); - } - - private void InitialReceives() - { - //progress update - Receive(stats => - { - //time to start animating the progress bar - if (!_hasSetProgress && stats.ExpectedUsers > 0) - { - _progressBar.Minimum = 0; - _progressBar.Step = 1; - _progressBar.Maximum = stats.ExpectedUsers; - _progressBar.Value = stats.UsersThusFar; - _progressBar.Visible = true; - _statusLabel.Visible = true; - } - - _statusLabel.Text = string.Format("{0} out of {1} users ({2} failures) [{3} elapsed]", - stats.UsersThusFar, stats.ExpectedUsers, stats.QueryFailures, stats.Elapsed); - _progressBar.Value = stats.UsersThusFar + stats.QueryFailures; - }); - - //user update - Receive>(repos => - { - foreach (var similarRepo in repos) - { - var repo = similarRepo.Repo; - var row = new DataGridViewRow(); - row.CreateCells(_userDg); - row.Cells[0].Value = repo.Owner.Login; - row.Cells[1].Value = repo.Name; - row.Cells[2].Value = repo.HtmlUrl; - row.Cells[3].Value = similarRepo.SharedStarrers; - row.Cells[4].Value = repo.SubscribersCount; - row.Cells[5].Value = repo.StargazersCount; - row.Cells[6].Value = repo.ForksCount; - _userDg.Rows.Add(row); - } - }); - - //critical failure, like not being able to connect to Github - Receive(failed => - { - _progressBar.Visible = true; - _progressBar.ForeColor = Color.Red; - _progressBar.Maximum = 1; - _progressBar.Value = 1; - _statusLabel.Visible = true; - _statusLabel.Text = string.Format("Failed to gather data for Github repository {0} / {1}", - failed.Repo.Owner, failed.Repo.Repo); - }); - } - } -} diff --git a/src/Unit-3/DoThis/App.config b/src/Unit-3/DoThis/App.config index 5958958f9..8a0a6bb76 100644 --- a/src/Unit-3/DoThis/App.config +++ b/src/Unit-3/DoThis/App.config @@ -10,14 +10,14 @@ - - - - Debug - AnyCPU - {93D0C836-98C9-45D1-B269-81220060DBB4} - WinExe - Properties - GithubActors - GithubActors - v4.5 - 512 - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - packages\Akka.0.8.0\lib\net45\Akka.dll - - - False - packages\Newtonsoft.Json.6.0.1\lib\net45\Newtonsoft.Json.dll - - - packages\Octokit.0.7.2\lib\net45\Octokit.dll - - - - - - - - - - - - - - - - - - - - - - Form - - - GithubAuth.cs - - - - - - - - - Form - - - LauncherForm.cs - - - - - Form - - - RepoResultsForm.cs - - - GithubAuth.cs - - - LauncherForm.cs - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - RepoResultsForm.cs - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - - \ No newline at end of file diff --git a/src/Unit-3/FSharpDoThis/GithubActors.fsproj b/src/Unit-3/DoThis/GithubActors.fsproj similarity index 100% rename from src/Unit-3/FSharpDoThis/GithubActors.fsproj rename to src/Unit-3/DoThis/GithubActors.fsproj diff --git a/src/Unit-3/DoThis/GithubActors.sln b/src/Unit-3/DoThis/GithubActors.sln index 3116f87b6..96648833c 100644 --- a/src/Unit-3/DoThis/GithubActors.sln +++ b/src/Unit-3/DoThis/GithubActors.sln @@ -1,9 +1,9 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.30723.0 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GithubActors", "GithubActors.csproj", "{93D0C836-98C9-45D1-B269-81220060DBB4}" +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "GithubActors", "GithubActors.fsproj", "{8F96BD67-3BC5-4702-B6D4-A0031B40ADD5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,10 +11,10 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {93D0C836-98C9-45D1-B269-81220060DBB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {93D0C836-98C9-45D1-B269-81220060DBB4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {93D0C836-98C9-45D1-B269-81220060DBB4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {93D0C836-98C9-45D1-B269-81220060DBB4}.Release|Any CPU.Build.0 = Release|Any CPU + {8F96BD67-3BC5-4702-B6D4-A0031B40ADD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8F96BD67-3BC5-4702-B6D4-A0031B40ADD5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8F96BD67-3BC5-4702-B6D4-A0031B40ADD5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8F96BD67-3BC5-4702-B6D4-A0031B40ADD5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Unit-3/DoThis/GithubAuth.Designer.cs b/src/Unit-3/DoThis/GithubAuth.Designer.cs deleted file mode 100644 index ad4b01a6f..000000000 --- a/src/Unit-3/DoThis/GithubAuth.Designer.cs +++ /dev/null @@ -1,115 +0,0 @@ -namespace GithubActors -{ - partial class GithubAuth - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.label1 = new System.Windows.Forms.Label(); - this.tbOAuth = new System.Windows.Forms.TextBox(); - this.lblAuthStatus = new System.Windows.Forms.Label(); - this.linkGhLabel = new System.Windows.Forms.LinkLabel(); - this.btnAuthenticate = new System.Windows.Forms.Button(); - this.SuspendLayout(); - // - // label1 - // - this.label1.AutoSize = true; - this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label1.Location = new System.Drawing.Point(12, 9); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(172, 18); - this.label1.TabIndex = 0; - this.label1.Text = "GitHub Access Token"; - // - // tbOAuth - // - this.tbOAuth.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.tbOAuth.Location = new System.Drawing.Point(190, 6); - this.tbOAuth.Name = "tbOAuth"; - this.tbOAuth.Size = new System.Drawing.Size(379, 24); - this.tbOAuth.TabIndex = 1; - // - // lblAuthStatus - // - this.lblAuthStatus.AutoSize = true; - this.lblAuthStatus.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.lblAuthStatus.Location = new System.Drawing.Point(187, 33); - this.lblAuthStatus.Name = "lblAuthStatus"; - this.lblAuthStatus.Size = new System.Drawing.Size(87, 18); - this.lblAuthStatus.TabIndex = 2; - this.lblAuthStatus.Text = "lblGHStatus"; - this.lblAuthStatus.Visible = false; - // - // linkGhLabel - // - this.linkGhLabel.AutoSize = true; - this.linkGhLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.linkGhLabel.Location = new System.Drawing.Point(148, 128); - this.linkGhLabel.Name = "linkGhLabel"; - this.linkGhLabel.Size = new System.Drawing.Size(273, 18); - this.linkGhLabel.TabIndex = 3; - this.linkGhLabel.Text = "How to get a GitHub Access Token"; - this.linkGhLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkGhLabel_LinkClicked); - // - // btnAuthenticate - // - this.btnAuthenticate.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.btnAuthenticate.Location = new System.Drawing.Point(214, 81); - this.btnAuthenticate.Name = "btnAuthenticate"; - this.btnAuthenticate.Size = new System.Drawing.Size(136, 32); - this.btnAuthenticate.TabIndex = 4; - this.btnAuthenticate.Text = "Authenticate"; - this.btnAuthenticate.UseVisualStyleBackColor = true; - this.btnAuthenticate.Click += new System.EventHandler(this.btnAuthenticate_Click); - // - // GithubAuth - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(582, 155); - this.Controls.Add(this.btnAuthenticate); - this.Controls.Add(this.linkGhLabel); - this.Controls.Add(this.lblAuthStatus); - this.Controls.Add(this.tbOAuth); - this.Controls.Add(this.label1); - this.Name = "GithubAuth"; - this.Text = "Sign in to GitHub"; - this.Load += new System.EventHandler(this.GithubAuth_Load); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.Label label1; - private System.Windows.Forms.TextBox tbOAuth; - private System.Windows.Forms.Label lblAuthStatus; - private System.Windows.Forms.LinkLabel linkGhLabel; - private System.Windows.Forms.Button btnAuthenticate; - } -} \ No newline at end of file diff --git a/src/Unit-3/DoThis/GithubAuth.cs b/src/Unit-3/DoThis/GithubAuth.cs deleted file mode 100644 index 872723e22..000000000 --- a/src/Unit-3/DoThis/GithubAuth.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Diagnostics; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; -using Akka.Actor; -using GithubActors.Actors; - -namespace GithubActors -{ - public partial class GithubAuth : Form - { - private ActorRef _authActor; - - public GithubAuth() - { - InitializeComponent(); - } - - private void GithubAuth_Load(object sender, EventArgs e) - { - linkGhLabel.Links.Add(new LinkLabel.Link() { LinkData = "https://help.github.com/articles/creating-an-access-token-for-command-line-use/" }); - _authActor = - Program.GithubActors.ActorOf(Props.Create(() => new GithubAuthenticationActor(lblAuthStatus, this)), ActorPaths.GithubAuthenticatorActor.Name); - } - - private void linkGhLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) - { - var link = e.Link.LinkData as string; - if (link != null) - { - //Send the URL to the operating system via windows shell - Process.Start(link); - } - } - - private void btnAuthenticate_Click(object sender, EventArgs e) - { - _authActor.Tell(new GithubAuthenticationActor.Authenticate(tbOAuth.Text)); - } - } -} diff --git a/src/Unit-3/DoThis/GithubAuth.resx b/src/Unit-3/DoThis/GithubAuth.resx deleted file mode 100644 index 61bc649ba..000000000 --- a/src/Unit-3/DoThis/GithubAuth.resx +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - True - - \ No newline at end of file diff --git a/src/Unit-3/FSharpDoThis/GithubAuthForm.fs b/src/Unit-3/DoThis/GithubAuthForm.fs similarity index 100% rename from src/Unit-3/FSharpDoThis/GithubAuthForm.fs rename to src/Unit-3/DoThis/GithubAuthForm.fs diff --git a/src/Unit-3/DoThis/GithubClientFactory.cs b/src/Unit-3/DoThis/GithubClientFactory.cs deleted file mode 100644 index 45c02bf65..000000000 --- a/src/Unit-3/DoThis/GithubClientFactory.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Octokit; -using Octokit.Internal; - -namespace GithubActors -{ - /// - /// Creates instances. - /// - public static class GithubClientFactory - { - /// - /// OAuth token - necessary to generate authenticated requests - /// and achieve non-terrible hourly API rate limit - /// - public static string OAuthToken { get; set; } - - public static GitHubClient GetUnauthenticatedClient() - { - return new GitHubClient(new ProductHeaderValue("AkkaBootcamp-Unit3")); - } - - public static GitHubClient GetClient() - { - return new GitHubClient(new ProductHeaderValue("AkkaBootcamp-Unit3"), new InMemoryCredentialStore(new Credentials(OAuthToken))); - } - } -} diff --git a/src/Unit-3/FSharpDoThis/GithubClientFactory.fs b/src/Unit-3/DoThis/GithubClientFactory.fs similarity index 100% rename from src/Unit-3/FSharpDoThis/GithubClientFactory.fs rename to src/Unit-3/DoThis/GithubClientFactory.fs diff --git a/src/Unit-3/DoThis/GithubProgressStats.cs b/src/Unit-3/DoThis/GithubProgressStats.cs deleted file mode 100644 index 267ce9278..000000000 --- a/src/Unit-3/DoThis/GithubProgressStats.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using Octokit; - -namespace GithubActors -{ - /// - /// used to sort the list of similar repos - /// - public class SimilarRepo : IComparable - { - public SimilarRepo(Repository repo) - { - Repo = repo; - } - - public Repository Repo { get; private set; } - - public int SharedStarrers { get; set; } - public int CompareTo(SimilarRepo other) - { - return SharedStarrers.CompareTo(other.SharedStarrers); - } - } - - /// - /// Used to report on incremental progress. - /// - /// Immutable. - /// - public class GithubProgressStats - { - public int ExpectedUsers { get; private set; } - public int UsersThusFar { get; private set; } - public int QueryFailures { get; private set; } - public DateTime StartTime { get; private set; } - public DateTime? EndTime { get; private set; } - - public TimeSpan Elapsed - { - get - { - return ((EndTime.HasValue ? EndTime.Value : DateTime.UtcNow) -StartTime); - } - } - - public bool IsFinished - { - get { return ExpectedUsers == UsersThusFar + QueryFailures; } - } - - public GithubProgressStats() - { - StartTime = DateTime.UtcNow; - } - - private GithubProgressStats(DateTime startTime, int expectedUsers, int usersThusFar, int queryFailures, DateTime? endTime) - { - EndTime = endTime; - QueryFailures = queryFailures; - UsersThusFar = usersThusFar; - ExpectedUsers = expectedUsers; - StartTime = startTime; - } - - /// - /// Add users to the running total of - /// - public GithubProgressStats UserQueriesFinished(int delta = 1) - { - return Copy(usersThusFar: UsersThusFar + delta); - } - - /// - /// Set the total - /// - public GithubProgressStats SetExpectedUserCount(int totalExpectedUsers) - { - return Copy(expectedUsers: totalExpectedUsers); - } - - /// - /// Add to the running total - /// - public GithubProgressStats IncrementFailures(int delta = 1) - { - return Copy(queryFailures: QueryFailures + delta); - } - - /// - /// Query is finished! Set's the - /// - public GithubProgressStats Finish() - { - return Copy(endTime: DateTime.UtcNow); - } - - /// - /// Creates a deep copy of the class - /// - public GithubProgressStats Copy(int? expectedUsers = null, int? usersThusFar = null, int? queryFailures = null, - DateTime? startTime = null, DateTime? endTime = null) - { - return new GithubProgressStats(startTime ?? StartTime, expectedUsers ?? ExpectedUsers, usersThusFar ?? UsersThusFar, - queryFailures ?? QueryFailures, endTime ?? EndTime); - } - } -} \ No newline at end of file diff --git a/src/Unit-3/DoThis/LauncherForm.Designer.cs b/src/Unit-3/DoThis/LauncherForm.Designer.cs deleted file mode 100644 index ac0259049..000000000 --- a/src/Unit-3/DoThis/LauncherForm.Designer.cs +++ /dev/null @@ -1,103 +0,0 @@ -namespace GithubActors -{ - partial class LauncherForm - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.tbRepoUrl = new System.Windows.Forms.TextBox(); - this.lblRepo = new System.Windows.Forms.Label(); - this.lblIsValid = new System.Windows.Forms.Label(); - this.btnLaunch = new System.Windows.Forms.Button(); - this.SuspendLayout(); - // - // tbRepoUrl - // - this.tbRepoUrl.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.tbRepoUrl.Location = new System.Drawing.Point(95, 13); - this.tbRepoUrl.Name = "tbRepoUrl"; - this.tbRepoUrl.Size = new System.Drawing.Size(455, 24); - this.tbRepoUrl.TabIndex = 0; - // - // lblRepo - // - this.lblRepo.AutoSize = true; - this.lblRepo.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.lblRepo.Location = new System.Drawing.Point(3, 16); - this.lblRepo.Name = "lblRepo"; - this.lblRepo.Size = new System.Drawing.Size(86, 18); - this.lblRepo.TabIndex = 1; - this.lblRepo.Text = "Repo URL"; - // - // lblIsValid - // - this.lblIsValid.AutoSize = true; - this.lblIsValid.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.lblIsValid.Location = new System.Drawing.Point(95, 44); - this.lblIsValid.Name = "lblIsValid"; - this.lblIsValid.Size = new System.Drawing.Size(46, 18); - this.lblIsValid.TabIndex = 2; - this.lblIsValid.Text = "label1"; - this.lblIsValid.Visible = false; - // - // btnLaunch - // - this.btnLaunch.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.btnLaunch.Location = new System.Drawing.Point(218, 90); - this.btnLaunch.Name = "btnLaunch"; - this.btnLaunch.Size = new System.Drawing.Size(142, 37); - this.btnLaunch.TabIndex = 3; - this.btnLaunch.Text = "GO"; - this.btnLaunch.UseVisualStyleBackColor = true; - this.btnLaunch.Click += new System.EventHandler(this.btnLaunch_Click); - // - // LauncherForm - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(562, 151); - this.Controls.Add(this.btnLaunch); - this.Controls.Add(this.lblIsValid); - this.Controls.Add(this.lblRepo); - this.Controls.Add(this.tbRepoUrl); - this.Name = "LauncherForm"; - this.Text = "Who Starred This Repo?"; - this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.LauncherForm_FormClosing); - this.Load += new System.EventHandler(this.MainForm_Load); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.TextBox tbRepoUrl; - private System.Windows.Forms.Label lblRepo; - private System.Windows.Forms.Label lblIsValid; - private System.Windows.Forms.Button btnLaunch; - } -} - diff --git a/src/Unit-3/DoThis/LauncherForm.cs b/src/Unit-3/DoThis/LauncherForm.cs deleted file mode 100644 index ff2361dcc..000000000 --- a/src/Unit-3/DoThis/LauncherForm.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Windows.Forms; -using Akka.Actor; -using GithubActors.Actors; - -namespace GithubActors -{ - public partial class LauncherForm : Form - { - private ActorRef _mainFormActor; - - public LauncherForm() - { - InitializeComponent(); - } - - private void MainForm_Load(object sender, EventArgs e) - { - /* INITIALIZE ACTORS */ - _mainFormActor = Program.GithubActors.ActorOf(Props.Create(() => new MainFormActor(lblIsValid)), ActorPaths.MainFormActor.Name); - Program.GithubActors.ActorOf(Props.Create(() => new GithubValidatorActor(GithubClientFactory.GetClient())), ActorPaths.GithubValidatorActor.Name); - Program.GithubActors.ActorOf(Props.Create(() => new GithubCommanderActor()), - ActorPaths.GithubCommanderActor.Name); - } - - private void btnLaunch_Click(object sender, EventArgs e) - { - _mainFormActor.Tell(new ProcessRepo(tbRepoUrl.Text)); - } - - private void LauncherForm_FormClosing(object sender, FormClosingEventArgs e) - { - Application.Exit(); - } - } -} diff --git a/src/Unit-3/FSharpDoThis/LauncherForm.fs b/src/Unit-3/DoThis/LauncherForm.fs similarity index 100% rename from src/Unit-3/FSharpDoThis/LauncherForm.fs rename to src/Unit-3/DoThis/LauncherForm.fs diff --git a/src/Unit-3/DoThis/LauncherForm.resx b/src/Unit-3/DoThis/LauncherForm.resx deleted file mode 100644 index 1af7de150..000000000 --- a/src/Unit-3/DoThis/LauncherForm.resx +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/src/Unit-3/DoThis/Messages.cs b/src/Unit-3/DoThis/Messages.cs deleted file mode 100644 index c3e5c1f77..000000000 --- a/src/Unit-3/DoThis/Messages.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace GithubActors -{ - /// - /// Begin processing a new Github repository for analysis - /// - public class ProcessRepo - { - public ProcessRepo(string repoUri) - { - RepoUri = repoUri; - } - - public string RepoUri { get; private set; } - } - - public class RepoKey - { - public RepoKey(string owner, string repo) - { - Repo = repo; - Owner = owner; - } - - public string Owner { get; private set; } - - public string Repo { get; private set; } - } - - - public class RetryableQuery - { - public RetryableQuery(object query, int allowableTries) : this(query, allowableTries, 0) - { - } - - private RetryableQuery(object query, int allowableTries, int currentAttempt) - { - AllowableTries = allowableTries; - Query = query; - CurrentAttempt = currentAttempt; - } - - - public object Query { get; private set; } - - public int AllowableTries { get; private set; } - - public int CurrentAttempt { get; private set; } - - public bool CanRetry { get { return RemainingTries > 0; } } - public int RemainingTries { get { return AllowableTries - CurrentAttempt; } } - - public RetryableQuery NextTry() - { - return new RetryableQuery(Query, AllowableTries, CurrentAttempt+1); - } - } -} diff --git a/src/Unit-3/FSharpDoThis/Messages.fs b/src/Unit-3/DoThis/Messages.fs similarity index 100% rename from src/Unit-3/FSharpDoThis/Messages.fs rename to src/Unit-3/DoThis/Messages.fs diff --git a/src/Unit-3/DoThis/Program.cs b/src/Unit-3/DoThis/Program.cs deleted file mode 100644 index fb7b9b28d..000000000 --- a/src/Unit-3/DoThis/Program.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Configuration; -using System.Windows.Forms; -using Akka.Actor; -using Akka.Configuration.Hocon; - -namespace GithubActors -{ - static class Program - { - /// - /// ActorSystem we'llbe using to collect and process data - /// from Github using their official .NET SDK, Octokit - /// - public static ActorSystem GithubActors; - - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - var section = (AkkaConfigurationSection)ConfigurationManager.GetSection("akka"); - var config = section.AkkaConfig; - GithubActors = ActorSystem.Create("GithubActors", config); - - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new GithubAuth()); - } - } -} diff --git a/src/Unit-3/FSharpDoThis/Program.fs b/src/Unit-3/DoThis/Program.fs similarity index 100% rename from src/Unit-3/FSharpDoThis/Program.fs rename to src/Unit-3/DoThis/Program.fs diff --git a/src/Unit-3/DoThis/Properties/AssemblyInfo.cs b/src/Unit-3/DoThis/Properties/AssemblyInfo.cs deleted file mode 100644 index 32c2a78eb..000000000 --- a/src/Unit-3/DoThis/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("GithubActors")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("GithubActors")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("4be30889-fb62-4047-8f7d-bbf5c70e2a68")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Unit-3/DoThis/Properties/Resources.Designer.cs b/src/Unit-3/DoThis/Properties/Resources.Designer.cs deleted file mode 100644 index 68d8e89af..000000000 --- a/src/Unit-3/DoThis/Properties/Resources.Designer.cs +++ /dev/null @@ -1,71 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.34014 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace GithubActors.Properties -{ - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GithubActors.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { - return resourceCulture; - } - set - { - resourceCulture = value; - } - } - } -} diff --git a/src/Unit-3/DoThis/Properties/Resources.resx b/src/Unit-3/DoThis/Properties/Resources.resx deleted file mode 100644 index af7dbebba..000000000 --- a/src/Unit-3/DoThis/Properties/Resources.resx +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/src/Unit-3/DoThis/Properties/Settings.Designer.cs b/src/Unit-3/DoThis/Properties/Settings.Designer.cs deleted file mode 100644 index bbc0bf47a..000000000 --- a/src/Unit-3/DoThis/Properties/Settings.Designer.cs +++ /dev/null @@ -1,30 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.34014 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace GithubActors.Properties -{ - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { - return defaultInstance; - } - } - } -} diff --git a/src/Unit-3/DoThis/Properties/Settings.settings b/src/Unit-3/DoThis/Properties/Settings.settings deleted file mode 100644 index 39645652a..000000000 --- a/src/Unit-3/DoThis/Properties/Settings.settings +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/Unit-3/DoThis/RepoResultsForm.Designer.cs b/src/Unit-3/DoThis/RepoResultsForm.Designer.cs deleted file mode 100644 index 0fa69019b..000000000 --- a/src/Unit-3/DoThis/RepoResultsForm.Designer.cs +++ /dev/null @@ -1,157 +0,0 @@ -namespace GithubActors -{ - partial class RepoResultsForm - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.dgUsers = new System.Windows.Forms.DataGridView(); - this.statusStrip1 = new System.Windows.Forms.StatusStrip(); - this.tsProgress = new System.Windows.Forms.ToolStripProgressBar(); - this.tsStatus = new System.Windows.Forms.ToolStripStatusLabel(); - this.Owner = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.RepoName = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.URL = new System.Windows.Forms.DataGridViewLinkColumn(); - this.Shared = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.Watchers = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.Stars = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.Forks = new System.Windows.Forms.DataGridViewTextBoxColumn(); - ((System.ComponentModel.ISupportInitialize)(this.dgUsers)).BeginInit(); - this.statusStrip1.SuspendLayout(); - this.SuspendLayout(); - // - // dgUsers - // - this.dgUsers.AllowUserToOrderColumns = true; - this.dgUsers.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; - this.dgUsers.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { - this.Owner, - this.RepoName, - this.URL, - this.Shared, - this.Watchers, - this.Stars, - this.Forks}); - this.dgUsers.Dock = System.Windows.Forms.DockStyle.Fill; - this.dgUsers.Location = new System.Drawing.Point(0, 0); - this.dgUsers.Name = "dgUsers"; - this.dgUsers.Size = new System.Drawing.Size(739, 322); - this.dgUsers.TabIndex = 0; - // - // statusStrip1 - // - this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.tsProgress, - this.tsStatus}); - this.statusStrip1.Location = new System.Drawing.Point(0, 300); - this.statusStrip1.Name = "statusStrip1"; - this.statusStrip1.Size = new System.Drawing.Size(739, 22); - this.statusStrip1.TabIndex = 1; - this.statusStrip1.Text = "statusStrip1"; - // - // tsProgress - // - this.tsProgress.Name = "tsProgress"; - this.tsProgress.Size = new System.Drawing.Size(100, 16); - this.tsProgress.Visible = false; - // - // tsStatus - // - this.tsStatus.Name = "tsStatus"; - this.tsStatus.Size = new System.Drawing.Size(73, 17); - this.tsStatus.Text = "Processing..."; - this.tsStatus.Visible = false; - // - // Owner - // - this.Owner.HeaderText = "Owner"; - this.Owner.Name = "Owner"; - // - // RepoName - // - this.RepoName.HeaderText = "Name"; - this.RepoName.Name = "RepoName"; - // - // URL - // - this.URL.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; - this.URL.HeaderText = "URL"; - this.URL.Name = "URL"; - // - // Shared - // - this.Shared.HeaderText = "SharedStars"; - this.Shared.Name = "Shared"; - // - // Watchers - // - this.Watchers.HeaderText = "Watchers"; - this.Watchers.Name = "Watchers"; - // - // Stars - // - this.Stars.HeaderText = "Stars"; - this.Stars.Name = "Stars"; - // - // Forks - // - this.Forks.HeaderText = "Forks"; - this.Forks.Name = "Forks"; - // - // RepoResultsForm - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(739, 322); - this.Controls.Add(this.statusStrip1); - this.Controls.Add(this.dgUsers); - this.Name = "RepoResultsForm"; - this.Text = "Repos Similar to {RepoName}"; - this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.RepoResultsForm_FormClosing); - this.Load += new System.EventHandler(this.RepoResultsForm_Load); - ((System.ComponentModel.ISupportInitialize)(this.dgUsers)).EndInit(); - this.statusStrip1.ResumeLayout(false); - this.statusStrip1.PerformLayout(); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.DataGridView dgUsers; - private System.Windows.Forms.StatusStrip statusStrip1; - private System.Windows.Forms.ToolStripProgressBar tsProgress; - private System.Windows.Forms.ToolStripStatusLabel tsStatus; - private System.Windows.Forms.DataGridViewTextBoxColumn Owner; - private System.Windows.Forms.DataGridViewTextBoxColumn RepoName; - private System.Windows.Forms.DataGridViewLinkColumn URL; - private System.Windows.Forms.DataGridViewTextBoxColumn Shared; - private System.Windows.Forms.DataGridViewTextBoxColumn Watchers; - private System.Windows.Forms.DataGridViewTextBoxColumn Stars; - private System.Windows.Forms.DataGridViewTextBoxColumn Forks; - } -} \ No newline at end of file diff --git a/src/Unit-3/DoThis/RepoResultsForm.cs b/src/Unit-3/DoThis/RepoResultsForm.cs deleted file mode 100644 index ee4dad6e2..000000000 --- a/src/Unit-3/DoThis/RepoResultsForm.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Windows.Forms; -using Akka.Actor; -using GithubActors.Actors; - -namespace GithubActors -{ - public partial class RepoResultsForm : Form - { - private ActorRef _formActor; - private ActorRef _githubCoordinator; - private RepoKey _repo; - - public RepoResultsForm(ActorRef githubCoordinator, RepoKey repo) - { - _githubCoordinator = githubCoordinator; - _repo = repo; - InitializeComponent(); - } - - private void RepoResultsForm_Load(object sender, System.EventArgs e) - { - _formActor = - Program.GithubActors.ActorOf( - Props.Create(() => new RepoResultsActor(dgUsers, tsStatus, tsProgress)) - .WithDispatcher("akka.actor.synchronized-dispatcher")); //run on the UI thread - - Text = string.Format("Repos Similar to {0} / {1}", _repo.Owner, _repo.Repo); - - //start subscribing to updates - _githubCoordinator.Tell(new GithubCoordinatorActor.SubscribeToProgressUpdates(_formActor)); - } - - private void RepoResultsForm_FormClosing(object sender, FormClosingEventArgs e) - { - //kill the form actor - _formActor.Tell(PoisonPill.Instance); - } - } -} diff --git a/src/Unit-3/FSharpDoThis/RepoResultsForm.fs b/src/Unit-3/DoThis/RepoResultsForm.fs similarity index 100% rename from src/Unit-3/FSharpDoThis/RepoResultsForm.fs rename to src/Unit-3/DoThis/RepoResultsForm.fs diff --git a/src/Unit-3/DoThis/RepoResultsForm.resx b/src/Unit-3/DoThis/RepoResultsForm.resx deleted file mode 100644 index eaea9d891..000000000 --- a/src/Unit-3/DoThis/RepoResultsForm.resx +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - 17, 17 - - \ No newline at end of file diff --git a/src/Unit-3/DoThis/packages.config b/src/Unit-3/DoThis/packages.config index d9dbe7767..c7fc4b7b2 100644 --- a/src/Unit-3/DoThis/packages.config +++ b/src/Unit-3/DoThis/packages.config @@ -1,6 +1,11 @@  - - - + + + + + + + + \ No newline at end of file diff --git a/src/Unit-3/FSharpDoThis/App.config b/src/Unit-3/FSharpDoThis/App.config deleted file mode 100644 index 8a0a6bb76..000000000 --- a/src/Unit-3/FSharpDoThis/App.config +++ /dev/null @@ -1,28 +0,0 @@ - - - -
- - - - - - - - - - \ No newline at end of file diff --git a/src/Unit-3/FSharpDoThis/GithubActors.sln b/src/Unit-3/FSharpDoThis/GithubActors.sln deleted file mode 100644 index 96648833c..000000000 --- a/src/Unit-3/FSharpDoThis/GithubActors.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "GithubActors", "GithubActors.fsproj", "{8F96BD67-3BC5-4702-B6D4-A0031B40ADD5}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8F96BD67-3BC5-4702-B6D4-A0031B40ADD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8F96BD67-3BC5-4702-B6D4-A0031B40ADD5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8F96BD67-3BC5-4702-B6D4-A0031B40ADD5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8F96BD67-3BC5-4702-B6D4-A0031B40ADD5}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/Unit-3/FSharpDoThis/packages.config b/src/Unit-3/FSharpDoThis/packages.config deleted file mode 100644 index c7fc4b7b2..000000000 --- a/src/Unit-3/FSharpDoThis/packages.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/Unit-3/lesson1/README.md b/src/Unit-3/lesson1/README.md index da8f63846..fc41cd4bf 100644 --- a/src/Unit-3/lesson1/README.md +++ b/src/Unit-3/lesson1/README.md @@ -181,7 +181,7 @@ Great! Now that you know what the different kinds of routers are, and how to use Recall that group routers do not create their routees, but instead are passed the `ActorPath`s of their routees. This means that those routees exist somewhere else in the hierarchy, and are managed by whatever other parent actors created them. -Practically, this means that a group router usually won't know that its routees have died. A group router will attempt to [`DeathWatch`](http://getakka.net/wiki/Supervision#what-lifecycle-monitoring-means) its routees, but it doesn't always succeed in subscribing. Much of this is due to the fact that `ActorPath`s can have wildcards. +Practically, this means that a group router usually won't know that its routees have died. A group router will attempt to [`DeathWatch`](http://getakka.net/articles/concepts/supervision.html#what-lifecycle-monitoring-means) its routees, but it doesn't always succeed in subscribing. Much of this is due to the fact that `ActorPath`s can have wildcards. #### Isn't it bad that group routers usually don't know their routees have died? Yes, it is bad. @@ -310,4 +310,4 @@ Awesome job! You've successfully used Akka.NET routers to achieve the first laye Come ask any questions you have, big or small, [in this ongoing Bootcamp chat with the Petabridge & Akka.NET teams](https://gitter.im/petabridge/akka-bootcamp). ### Problems with the code? -If there is a problem with the code running, or something else that needs to be fixed in this lesson, please [create an issue](/issues) and we'll get right on it. This will benefit everyone going through Bootcamp. +If there is a problem with the code running, or something else that needs to be fixed in this lesson, please [create an issue](https://github.com/petabridge/akka-bootcamp/issues) and we'll get right on it. This will benefit everyone going through Bootcamp. diff --git a/src/Unit-3/lesson2/README.md b/src/Unit-3/lesson2/README.md index d6a29c575..4ed74ba93 100644 --- a/src/Unit-3/lesson2/README.md +++ b/src/Unit-3/lesson2/README.md @@ -136,4 +136,4 @@ Now that we've seen how both `Group` and `Pool` routers work, it's easy for us t Come ask any questions you have, big or small, [in this ongoing Bootcamp chat with the Petabridge & Akka.NET teams](https://gitter.im/petabridge/akka-bootcamp). ### Problems with the code? -If there is a problem with the code running, or something else that needs to be fixed in this lesson, please [create an issue](/issues) and we'll get right on it. This will benefit everyone going through Bootcamp. +If there is a problem with the code running, or something else that needs to be fixed in this lesson, please [create an issue](https://github.com/petabridge/akka-bootcamp/issues) and we'll get right on it. This will benefit everyone going through Bootcamp. diff --git a/src/Unit-3/lesson3/README.md b/src/Unit-3/lesson3/README.md index 85e8aa9a2..fa12ac589 100644 --- a/src/Unit-3/lesson3/README.md +++ b/src/Unit-3/lesson3/README.md @@ -8,7 +8,7 @@ Now we need to show you how to configure and deploy them :) #### Quick review of HOCON We first learned about HOCON in [Lesson 2.1](../../Unit-2/lesson1/). -To review, [HOCON (Human-Optimized Config Object Notation)](http://getakka.net/wiki/HOCON) is a flexible and extensible configuration format. It will allow you to configure everything from Akka.NET's `ActorRefProvider` implementation, logging, network transports, and more commonly - how individual actors are deployed. +To review, [HOCON (Human-Optimized Config Object Notation)](http://getakka.net/articles/concepts/configuration.html#what-is-hocon) is a flexible and extensible configuration format. It will allow you to configure everything from Akka.NET's `ActorRefProvider` implementation, logging, network transports, and more commonly - how individual actors are deployed. It's this last feature that we'll be using here to configure how our router actors are deployed. An actor is "deployed" when it is instantiated and put into service within the `ActorSystem` somewhere. @@ -247,4 +247,4 @@ We've been able to leverage routers for parallelism both via explicit programmat Come ask any questions you have, big or small, [in this ongoing Bootcamp chat with the Petabridge & Akka.NET teams](https://gitter.im/petabridge/akka-bootcamp). ### Problems with the code? -If there is a problem with the code running, or something else that needs to be fixed in this lesson, please [create an issue](/issues) and we'll get right on it. This will benefit everyone going through Bootcamp. +If there is a problem with the code running, or something else that needs to be fixed in this lesson, please [create an issue](https://github.com/petabridge/akka-bootcamp/issues) and we'll get right on it. This will benefit everyone going through Bootcamp. diff --git a/src/Unit-3/lesson4/README.md b/src/Unit-3/lesson4/README.md index 2dac6a0dc..18d35f00c 100644 --- a/src/Unit-3/lesson4/README.md +++ b/src/Unit-3/lesson4/README.md @@ -362,4 +362,4 @@ See our [full Akka.NET `PipeTo` sample in C#](https://github.com/petabridge/akka Come ask any questions you have, big or small, [in this ongoing Bootcamp chat with the Petabridge & Akka.NET teams](https://gitter.im/petabridge/akka-bootcamp). ### Problems with the code? -If there is a problem with the code running, or something else that needs to be fixed in this lesson, please [create an issue](/issues) and we'll get right on it. This will benefit everyone going through Bootcamp. +If there is a problem with the code running, or something else that needs to be fixed in this lesson, please [create an issue](https://github.com/petabridge/akka-bootcamp/issues) and we'll get right on it. This will benefit everyone going through Bootcamp. diff --git a/src/Unit-3/lesson5/README.md b/src/Unit-3/lesson5/README.md index f25101a4e..21708a811 100644 --- a/src/Unit-3/lesson5/README.md +++ b/src/Unit-3/lesson5/README.md @@ -206,5 +206,5 @@ Petabridge co-founders Come ask any questions you have, big or small, [in this ongoing Bootcamp chat with the Petabridge & Akka.NET teams](https://gitter.im/petabridge/akka-bootcamp). ### Problems with the code? -If there is a problem with the code running, or something else that needs to be fixed in this lesson, please [create an issue](/issues) and we'll get right on it. This will benefit everyone going through Bootcamp. +If there is a problem with the code running, or something else that needs to be fixed in this lesson, please [create an issue](https://github.com/petabridge/akka-bootcamp/issues) and we'll get right on it. This will benefit everyone going through Bootcamp.