├── .java-version ├── project └── build.properties ├── .travis.yml ├── src └── main │ ├── resources │ ├── keys │ │ ├── server.p12 │ │ ├── exampletrust.jks │ │ ├── server.crt │ │ ├── rootCA.crt │ │ ├── README.md │ │ ├── server.key │ │ ├── rootCA.key │ │ └── chain.pem │ ├── logfile.txt │ └── application.conf │ └── java │ └── sample │ └── stream │ ├── BasicTransformation.java │ ├── GroupLogFile.java │ ├── WritePrimes.java │ ├── TcpEcho.java │ └── TcpTLSEcho.java ├── LICENSE ├── README.md ├── activator.properties ├── .gitignore ├── COPYING └── tutorial └── index.html /.java-version: -------------------------------------------------------------------------------- 1 | 1.8 2 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.8 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | jdk: 3 | - oraclejdk8 4 | scala: 5 | - 2.11.7 -------------------------------------------------------------------------------- /src/main/resources/keys/server.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesafehub/activator-akka-stream-java8/HEAD/src/main/resources/keys/server.p12 -------------------------------------------------------------------------------- /src/main/resources/keys/exampletrust.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesafehub/activator-akka-stream-java8/HEAD/src/main/resources/keys/exampletrust.jks -------------------------------------------------------------------------------- /src/main/resources/logfile.txt: -------------------------------------------------------------------------------- 1 | 1. [INFO] starting system 2 | 2. [DEBUG] starting subsystem 1 3 | 3. [ERROR] could not initalize subsystem 1 4 | 4. [DEBUG] starting subsystem 2 5 | 5. [ERROR] could not initalize subsystem 2 6 | 6. something else 7 | 7. [WARN] bad sign 8 | 8. [INFO] aright, sorted it out 9 | 9. [DEBUG] working... 10 | 10. [DEBUG] all done 11 | 11. [INFO] shutting down system -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Activator Template by Typesafe 2 | 3 | Licensed under Public Domain (CC0) 4 | 5 | To the extent possible under law, the person who associated CC0 with 6 | this Activator Tempate has waived all copyright and related or neighboring 7 | rights to this Activator Template. 8 | 9 | You should have received a copy of the CC0 legalcode along with this 10 | work. If not, see . -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Akka streams activator template 2 | =============================== 3 | 4 | [![Build Status](https://travis-ci.org/typesafehub/activator-akka-stream-java8.svg?branch=master)](https://travis-ci.org/typesafehub/activator-akka-stream-java8) 5 | 6 | Demonstrate Akka streams in Java8. 7 | 8 | Akka Streams is an implementation of [Reactive Streams](http://www.reactive-streams.org/), 9 | which is a standard for asynchronous stream processing with non-blocking backpressure. 10 | 11 | -------------------------------------------------------------------------------- /src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | akka { 2 | loglevel = "DEBUG" 3 | 4 | ssl-config.ssl { 5 | loose.acceptAnyCertificate = true 6 | 7 | debug { 8 | certpath = true 9 | ocsp = true 10 | } 11 | 12 | trustManager = { 13 | stores = [ 14 | { 15 | type = "JKS", 16 | path = "/Users/ktoso/code/activator-akka-stream-java8/src/main/resources/keys/exampletrust.jks", 17 | pass = "abcdef" 18 | } 19 | ] 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /activator.properties: -------------------------------------------------------------------------------- 1 | name=akka-stream-java8 2 | title=Akka Streams with Java8! 3 | description=Demonstrates Akka Streams 4 | tags=akka,java8,sample 5 | 6 | authorName=Typesafe 7 | authorLink=http://typesafe.com/ 8 | authorTwitter=typesafe 9 | authorBio=Typesafe is dedicated to helping developers build reactive applications on the JVM. With the Typesafe Reactive Platform, including Play Framework, Akka, and Scala, developers can deliver highly responsive user experiences backed by a resilient and event-driven application stack that scales effortlessly on multicore and cloud computing architectures. 10 | authorLogo=http://typesafe.com/assets/images/activator/logo-typesafe-activator.png 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *# 2 | *.iml 3 | *.ipr 4 | *.iws 5 | *.pyc 6 | *.tm.epoch 7 | *.vim 8 | */project/boot 9 | */project/build/target 10 | */project/project.target.config-classes 11 | *-shim.sbt 12 | *~ 13 | .#* 14 | .*.swp 15 | .DS_Store 16 | .cache 17 | .cache 18 | .classpath 19 | .codefellow 20 | .ensime* 21 | .eprj 22 | .history 23 | .idea 24 | .manager 25 | .multi-jvm 26 | .project 27 | .scala_dependencies 28 | .scalastyle 29 | .settings 30 | .tags 31 | .tags_sorted_by_file 32 | .target 33 | .worksheet 34 | Makefile 35 | TAGS 36 | lib_managed 37 | logs 38 | project/boot/* 39 | project/plugins/project 40 | src_managed 41 | target 42 | tm*.lck 43 | tm*.log 44 | tm.out 45 | worker*.log 46 | /bin 47 | -------------------------------------------------------------------------------- /src/main/resources/keys/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDITCCAgkCCQCo8H6OcPrArzANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB 3 | VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 4 | cyBQdHkgTHRkMB4XDTE1MDcyMzA5NTEyMloXDTI1MDQyMTA5NTEyMlowYDELMAkG 5 | A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 6 | IFdpZGdpdHMgUHR5IEx0ZDEZMBcGA1UEAwwQYWtrYS5leGFtcGxlLm9yZzCCASIw 7 | DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANMy/wgrSYVhVtu9OGbo2rSKauiz 8 | 5V56X4uCqtCHF9UeHtnVtFLCBMa+pimOS+UyUAT4mbBsxW22BhoNUBZ15KPxltyD 9 | yEsqNCKwWGxL3r8AXQtze2MEpTl22Lvp/iCTXO1vbML/+9r3uqUjw/AAP9HwF9Wd 10 | j/yOrs6q8WE4sfc48iOj6N60/h2pRfn2WNJmo9W9FLC53NznixfsG5oN6Jmb9RM+ 11 | fMHYXLfL/Vt6NrgVX1uqHt9HvuoxfNKhhXE5VU8bNfFfzPYvIt4aZXGxO15vEqsq 12 | OaZ7YJyKr1oFfJC8LmE5xPa3GHToCqmkdMXQK38mpslMQWlQLYnmkS5Qzv8CAwEA 13 | ATANBgkqhkiG9w0BAQsFAAOCAQEAEPDd1gAF9q2LtoZqTdcwmeBjdbT7n0WDRSuI 14 | BzQ/qKjvymwpFKQ0pZSPUyaw2qfRRiTQ/QTbqYep2mhvl5n+gW3ifTp83zgTGKH/ 15 | 3sDlX0HPSCBYCDy2gP/AOIgV/57ADMpEkTlz8yyLMH+pLDAoNFIPwy7blAkq+ULQ 16 | y6TfEBmZXoemSaIh5tRnexCD+pTvL4MRrGlBEoxdejDnIAt4n6BxmF0b4hKg8uta 17 | UvivA85lBKzWUoR/Vam5/SC8jtcyLt9RThRcNSj6zP6s5d+o+8PLznrSEadAtfD9 18 | 0q+t4TYF81tClEEgGruVPNL4WIpDniOfw9AJgQNVJGfy5TKY1Q== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /src/main/java/sample/stream/BasicTransformation.java: -------------------------------------------------------------------------------- 1 | package sample.stream; 2 | 3 | import java.io.IOException; 4 | import java.util.Arrays; 5 | 6 | import akka.NotUsed; 7 | import scala.runtime.BoxedUnit; 8 | import akka.actor.ActorSystem; 9 | import akka.dispatch.OnComplete; 10 | import akka.stream.ActorMaterializer; 11 | import akka.stream.javadsl.Source; 12 | 13 | public class BasicTransformation { 14 | public static void main(String[] args) throws IOException { 15 | final ActorSystem system = ActorSystem.create("Sys"); 16 | final ActorMaterializer materializer = ActorMaterializer.create(system); 17 | 18 | final String text = 19 | "Lorem Ipsum is simply dummy text of the printing and typesetting industry. " + 20 | "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, " + 21 | "when an unknown printer took a galley of type and scrambled it to make a type " + 22 | "specimen book."; 23 | 24 | Source.from(Arrays.asList(text.split("\\s"))). 25 | // transform 26 | map(e -> e.toUpperCase()). 27 | // print to console 28 | runForeach(System.out::println, materializer). 29 | handle((done, failure) -> { 30 | system.terminate(); 31 | return NotUsed.getInstance(); 32 | }); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/resources/keys/rootCA.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDXTCCAkWgAwIBAgIJANYwx08wP3STMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV 3 | BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX 4 | aWRnaXRzIFB0eSBMdGQwHhcNMTUwNzIzMDk0ODI2WhcNMjUwNDIxMDk0ODI2WjBF 5 | MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 6 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 7 | CgKCAQEArk0K/Rn7uND2YGFBks5Sok1WvNdHQccPESEw2hNVF32ExAhbBXCrFaIl 8 | Io0q4eYSbypeauEjDXB/NJXurEefL8ONXK62erJDKKQ0aTTYqsVifoNYA9ORWoGE 9 | XhtAfOx4xvzr6vF1e3kz0PB/A4ftn0vvVygYnf/2E2bQZgaw8dXP5lIGasEzzigB 10 | LX/qTEW/vBOL98Rxp6JvjwvYMbPSZGwNwSz+tI5W2psdE1Mga2Qnsv3j+STWlD9v 11 | +JlgdN8r3PyR1sl3jC7gCj3AaOhv4RbAbqjwnZ9nrckx16PFiMtJiVRea7CQXN7g 12 | 191EVujQnlg1LOhiSMKwVsuoXr08ywIDAQABo1AwTjAdBgNVHQ4EFgQU2THI/ilU 13 | M0xds3vZlV4CvhAZ1d8wHwYDVR0jBBgwFoAU2THI/ilUM0xds3vZlV4CvhAZ1d8w 14 | DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAK9LO0HyIi0xbTISsc+A5 15 | LQyZowgRAGqsNNmni7NKDXauPLZrCfDVhvo/FPP1XSFShXo7ARvro9lul4AJlkNN 16 | VgX0gbWtkiAx0uLqlbMsC6imj2L9boRse7mzI/Ymem5SNTn9GUnlMiZ74rca9UT4 17 | Dk9YytrT4FSpomiL6z8Xj604W3RuLSdEfpfcn3Jh2tFSZ9hyLwB7ATUTA/yuj1SU 18 | G1gmoPMvlnPzNj2lIqyIdQxGdxt+L3mFO20CxBkeieWqQuNptpjwptliFjkZJJZP 19 | wQlx9qLLvs/eFC2AUWj+hbsl37PuARR9hoeqbKRcUjwGtaXOqikrvX1qzPc2+ij9 20 | /w== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /src/main/resources/keys/README.md: -------------------------------------------------------------------------------- 1 | Keys for running Tls tests using the `ExampleHttpContexts` 2 | ---------------------------------------------------------- 3 | 4 | Instructions adapted from 5 | 6 | * http://datacenteroverlords.com/2012/03/01/creating-your-own-ssl-certificate-authority/ 7 | * http://security.stackexchange.com/questions/9600/how-to-use-openssl-generated-keys-in-java 8 | 9 | 10 | # Create a rootCA key: 11 | 12 | ``` 13 | openssl genrsa -out rootCA.key 2048 14 | ``` 15 | 16 | # Self-sign CA: 17 | 18 | ``` 19 | openssl req -x509 -new -nodes -key rootCA.key -days 3560 -out rootCA.crt 20 | ``` 21 | 22 | # Create server key: 23 | 24 | ``` 25 | openssl genrsa -out server.key 2048 26 | ``` 27 | 28 | # Create server CSR (you need to set the common name CN to "akka.example.org"): 29 | 30 | ``` 31 | openssl req -new -key server.key -out server.csr 32 | ``` 33 | 34 | # Create server certificate: 35 | 36 | ``` 37 | openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server.crt -days 3560 38 | ``` 39 | 40 | # Create certificate chain: 41 | 42 | ``` 43 | cat server.crt rootCA.crt > chain.pem 44 | ``` 45 | 46 | # Convert certificate and key to pkcs12 (you need to provide a password manually, `ExampleHttpContexts` 47 | # expects the password to be "abcdef"): 48 | 49 | ``` 50 | openssl pkcs12 -export -name servercrt -in chain.pem -inkey server.key -out server.p12 51 | ``` 52 | 53 | # For investigating remote certs use: 54 | 55 | ``` 56 | openssl s_client -showcerts -connect 54.173.126.144:443 57 | ``` 58 | -------------------------------------------------------------------------------- /src/main/resources/keys/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEA0zL/CCtJhWFW2704ZujatIpq6LPlXnpfi4Kq0IcX1R4e2dW0 3 | UsIExr6mKY5L5TJQBPiZsGzFbbYGGg1QFnXko/GW3IPISyo0IrBYbEvevwBdC3N7 4 | YwSlOXbYu+n+IJNc7W9swv/72ve6pSPD8AA/0fAX1Z2P/I6uzqrxYTix9zjyI6Po 5 | 3rT+HalF+fZY0maj1b0UsLnc3OeLF+wbmg3omZv1Ez58wdhct8v9W3o2uBVfW6oe 6 | 30e+6jF80qGFcTlVTxs18V/M9i8i3hplcbE7Xm8Sqyo5pntgnIqvWgV8kLwuYTnE 7 | 9rcYdOgKqaR0xdArfyamyUxBaVAtieaRLlDO/wIDAQABAoIBADfqTXkVNM7aWYut 8 | yiv8xEJ+TxWy4ywjS/58psq0qYukANj9alNqyKbxvL5NzSwuKN9YDiCWe6KzSWRG 9 | WAjKR7Fb+ewB+9pinxD8DT0GzT9WUkwA1A8AINpY68K8jaqEOVsnX+00prJvWfv0 10 | vyBggIUNgtHseD2ObRuMSIHL59oivxoBKmeRqFl26PCq+m6Dp1SsMwL8NE02rfUu 11 | uVW0zSz0/A5ZK90l8St3N78Puw/qicvfrI4PrGi4kLKW9UKJKP5FzfPF7Kf9itVA 12 | 1VB3gd8Gs98vRnzHwZlwgjyAQkePzS/iEQid9uRA/Xys5ozcT1arYM00t3I7ZEUg 13 | GJTKHBECgYEA+K/M6smzPrTAi0BEuI1NCb3zfxkjbBhC0cco9U4VIuhYVU+7Ukre 14 | zi5yI+BQR8MPbftSeeosXV6eQaq04pKCrHWF+ql+3Io9Hojghd/EnNCOtGxjTGmI 15 | Px8G7byeIr4+QyP+JSEdsVBfIEEQ9BJ8Up84RibsMfWcKe6ntzAMEmkCgYEA2Wj6 16 | DqPisPp4WwGi8bSvSRZsF3h3xu0saml+ug28j+b3kOa99Uz49kCi99sacJArYOWv 17 | Dn+DPl2K2/lwYO0bfyXwWaLp8pd/MAmwhKZ2+qvoUnkZJFRU3yrUoPp7CURZSbcG 18 | aD7IKotFH7wutqj8pZ50y8VGqKVACenhRSAH2ScCgYAuX7IJslUfg1tIXFK0S30r 19 | LOXENK7bUGbdcZMcs1PTr5oRRo362YVU02prcD/oMeKlsrD9lQJy4tsGCcwzV/jQ 20 | KhYy2PqUK58cG5AqxsCGMYn68R9PN3q1spZ7LKocdndr08FnsRY1Y3Rpslhz+yJ9 21 | 0b0Pr+BprJBTbXKPAYGuyQKBgAJFu59djSgGZi2lVburBM4Bwv13z+CvZ/Bwy9dL 22 | /3WNl3bXQpMGy+9e+5UVoDAfAaUQoYTIRmnndmUYNVl+APSSQ/Hb5xAXD0hEQakR 23 | SFsUYuhBxcaAbyap/vDzzUdqhHhlxlZemZ8AN6e+Qsq793APuO7MUBHBMGsqG6Wq 24 | UQqvAoGAINEINXhFXp2qVRDBUY57rRtpjQHajeNTMChgWTg30owfVNBY4evjRj8f 25 | 9XDuUkTumYcDcnOKmX3L6n9rg4noHlfNvxmn9pmG9vP0mG0MEOOxSxXFHVIuBw10 26 | wdTb0WE/i3FhyufdaRHLGhPAMQjaCeFSV3sMxMHuNePvCxnKD3E= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /src/main/resources/keys/rootCA.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEArk0K/Rn7uND2YGFBks5Sok1WvNdHQccPESEw2hNVF32ExAhb 3 | BXCrFaIlIo0q4eYSbypeauEjDXB/NJXurEefL8ONXK62erJDKKQ0aTTYqsVifoNY 4 | A9ORWoGEXhtAfOx4xvzr6vF1e3kz0PB/A4ftn0vvVygYnf/2E2bQZgaw8dXP5lIG 5 | asEzzigBLX/qTEW/vBOL98Rxp6JvjwvYMbPSZGwNwSz+tI5W2psdE1Mga2Qnsv3j 6 | +STWlD9v+JlgdN8r3PyR1sl3jC7gCj3AaOhv4RbAbqjwnZ9nrckx16PFiMtJiVRe 7 | a7CQXN7g191EVujQnlg1LOhiSMKwVsuoXr08ywIDAQABAoIBAQCSXAEpLMNRmq33 8 | mlMMqhF7VcPKyF5+Xl9Je/xgcjFWi0CLt5Ruyf/vJ3tVOwLSM3YxQHuN9cSQSXGX 9 | P3rt0SpbWjJ+q/pwpvV7z/5uhUCWjS46m6GxfNsmC3GR8AJDo/F67fBQFTcYWlrn 10 | TLrqxR4EUCgGoJWjPsZr3j6KHX5BYmzyTuJFBzxxipK42hnJQ7tMB8l6/5r4nRka 11 | d6SGFpJDkyhO+Wl0sBXjxHu1E4g8asI061jEOhcROV1Dk4hp1CYhd8TBj//6FSBC 12 | ttsIe2gxT0fk8bnNC78FuO0CUTCj4hFOWP7apr/NhLlxypu+4hj17NMhlptRvGxz 13 | 6pPlMVDJAoGBANPVTS5nkJpMyczA5vaHsyTF/nwunogwHVeVYsQQ3Bed28Ldp7gr 14 | Dr4hgYFvGkEmlLvWOleHvGISuD3lHLd112LcPyLFMRrs8wX9vWTueZGYj5KDLS3C 15 | i3GaYMqqYbuiFY1QYprF36zRQkLMKUiOomE2+baCasbhluAqqx32KEKvAoGBANKk 16 | cG0X0svJ/TTQIE5nfDtKePDUA7wEPYGrQOO4vKKZUlytVhf+gEcYr575bPjkTl1h 17 | 5jrrhr4OWpFDmRyBpi7wB95Fe93Df+0o4KmiNtsioZsi/MA5Tga2rAZPBBuZ9+5l 18 | alYl0fTo5PR3fOXJJoJ+w7+QI4N/9TGuBJoiEl6lAoGBAM8XapsBOIcApxB7TdCa 19 | HXLH9eDlmqq9jxH+w022xdR4yU2acMtFnOYXz4oAWgRzeVihOOw1kN+4OVKZWBer 20 | JuRJOZf+e+E84OFsjOnNkh/arBGqGFLyLGzlZdb79wv+i19ZxOxWojNLaKHxAjMi 21 | 7nBn1Hyux0CjbmK8lAl4iyeVAoGAT6r4BprTFFaiGN56yYykVPx2v4dAnlTwOmHe 22 | GgLd/ZWFrB23CT4toDY6/iKST5Rx+ymy3SgFf06IfJaXi0uR4gDQyQV4sshlUvp5 23 | 9k6u9rSjcLyL4dwKoclnSL+L6zCRsC3VSR3myf1n0vp6V6J7mTF+sa4/cFXuE8sg 24 | XHd0gS0CgYAXNDcF+zYoSmbfdG7uM7qOPQwNRbr0pHvAg0NmtM9JOj8gZPoaeAy3 25 | 3jEk9AMQrK0MNsRynAoMkhy+7WOU6TNLvyxXAKGZffOmABzSB9LEFgHkVPutl5/i 26 | wL2pE1SoG2QwSqFYGv+rHgIpREJzDTNwbmSbl/Za50JrIZ3OFfTMDQ== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /src/main/java/sample/stream/GroupLogFile.java: -------------------------------------------------------------------------------- 1 | package sample.stream; 2 | 3 | import akka.NotUsed; 4 | import akka.actor.ActorSystem; 5 | import akka.japi.Pair; 6 | import akka.stream.ActorMaterializer; 7 | import akka.stream.javadsl.FileIO; 8 | import akka.stream.javadsl.Framing; 9 | import akka.stream.javadsl.FramingTruncation; 10 | import akka.util.ByteString; 11 | 12 | import java.io.File; 13 | import java.io.FileOutputStream; 14 | import java.io.IOException; 15 | import java.io.PrintWriter; 16 | import java.util.Arrays; 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | import java.util.regex.Matcher; 20 | import java.util.regex.Pattern; 21 | 22 | public class GroupLogFile { 23 | public static void main(String[] args) throws IOException { 24 | final ActorSystem system = ActorSystem.create("Sys"); 25 | final ActorMaterializer materializer = ActorMaterializer.create(system); 26 | 27 | final Pattern loglevelPattern = Pattern.compile(".*\\[(DEBUG|INFO|WARN|ERROR)\\].*"); 28 | 29 | // read lines from a log file 30 | final String inPath = "src/main/resources/logfile.txt"; 31 | final File inputFile = new File(inPath); 32 | 33 | final Map outputs = new HashMap<>(); 34 | for (String level : Arrays.asList("DEBUG", "INFO", "WARN", "ERROR", "UNKNOWN")) { 35 | final String outPath = "target/log-" + level + ".txt"; 36 | final PrintWriter output = new PrintWriter(new FileOutputStream(outPath), true); 37 | outputs.put(level, output); 38 | } 39 | 40 | FileIO.fromFile(inputFile). 41 | // parse bytestrings (chunks of data) to lines 42 | via(Framing.delimiter(ByteString.fromString(System.lineSeparator()), 512, FramingTruncation.ALLOW)). 43 | map(ByteString::utf8String). 44 | // group them by log level 45 | map((line) -> { 46 | final Matcher matcher = loglevelPattern.matcher(line); 47 | if (matcher.find()) 48 | return new Pair<>(matcher.group(1), line); 49 | else 50 | return new Pair<>("UNKNOWN", line); 51 | }). 52 | // write lines of each group to a separate file 53 | runForeach((levelProducerPair) -> { 54 | outputs.get(levelProducerPair.first()).println(levelProducerPair.second()); 55 | }, materializer).handle((ignored, failure) -> { 56 | outputs.forEach((key, writer) -> { 57 | writer.close(); 58 | }); 59 | system.terminate(); 60 | return NotUsed.getInstance(); 61 | }); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/resources/keys/chain.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDITCCAgkCCQCo8H6OcPrArzANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB 3 | VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 4 | cyBQdHkgTHRkMB4XDTE1MDcyMzA5NTEyMloXDTI1MDQyMTA5NTEyMlowYDELMAkG 5 | A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 6 | IFdpZGdpdHMgUHR5IEx0ZDEZMBcGA1UEAwwQYWtrYS5leGFtcGxlLm9yZzCCASIw 7 | DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANMy/wgrSYVhVtu9OGbo2rSKauiz 8 | 5V56X4uCqtCHF9UeHtnVtFLCBMa+pimOS+UyUAT4mbBsxW22BhoNUBZ15KPxltyD 9 | yEsqNCKwWGxL3r8AXQtze2MEpTl22Lvp/iCTXO1vbML/+9r3uqUjw/AAP9HwF9Wd 10 | j/yOrs6q8WE4sfc48iOj6N60/h2pRfn2WNJmo9W9FLC53NznixfsG5oN6Jmb9RM+ 11 | fMHYXLfL/Vt6NrgVX1uqHt9HvuoxfNKhhXE5VU8bNfFfzPYvIt4aZXGxO15vEqsq 12 | OaZ7YJyKr1oFfJC8LmE5xPa3GHToCqmkdMXQK38mpslMQWlQLYnmkS5Qzv8CAwEA 13 | ATANBgkqhkiG9w0BAQsFAAOCAQEAEPDd1gAF9q2LtoZqTdcwmeBjdbT7n0WDRSuI 14 | BzQ/qKjvymwpFKQ0pZSPUyaw2qfRRiTQ/QTbqYep2mhvl5n+gW3ifTp83zgTGKH/ 15 | 3sDlX0HPSCBYCDy2gP/AOIgV/57ADMpEkTlz8yyLMH+pLDAoNFIPwy7blAkq+ULQ 16 | y6TfEBmZXoemSaIh5tRnexCD+pTvL4MRrGlBEoxdejDnIAt4n6BxmF0b4hKg8uta 17 | UvivA85lBKzWUoR/Vam5/SC8jtcyLt9RThRcNSj6zP6s5d+o+8PLznrSEadAtfD9 18 | 0q+t4TYF81tClEEgGruVPNL4WIpDniOfw9AJgQNVJGfy5TKY1Q== 19 | -----END CERTIFICATE----- 20 | -----BEGIN CERTIFICATE----- 21 | MIIDXTCCAkWgAwIBAgIJANYwx08wP3STMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV 22 | BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX 23 | aWRnaXRzIFB0eSBMdGQwHhcNMTUwNzIzMDk0ODI2WhcNMjUwNDIxMDk0ODI2WjBF 24 | MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 25 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 26 | CgKCAQEArk0K/Rn7uND2YGFBks5Sok1WvNdHQccPESEw2hNVF32ExAhbBXCrFaIl 27 | Io0q4eYSbypeauEjDXB/NJXurEefL8ONXK62erJDKKQ0aTTYqsVifoNYA9ORWoGE 28 | XhtAfOx4xvzr6vF1e3kz0PB/A4ftn0vvVygYnf/2E2bQZgaw8dXP5lIGasEzzigB 29 | LX/qTEW/vBOL98Rxp6JvjwvYMbPSZGwNwSz+tI5W2psdE1Mga2Qnsv3j+STWlD9v 30 | +JlgdN8r3PyR1sl3jC7gCj3AaOhv4RbAbqjwnZ9nrckx16PFiMtJiVRea7CQXN7g 31 | 191EVujQnlg1LOhiSMKwVsuoXr08ywIDAQABo1AwTjAdBgNVHQ4EFgQU2THI/ilU 32 | M0xds3vZlV4CvhAZ1d8wHwYDVR0jBBgwFoAU2THI/ilUM0xds3vZlV4CvhAZ1d8w 33 | DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAK9LO0HyIi0xbTISsc+A5 34 | LQyZowgRAGqsNNmni7NKDXauPLZrCfDVhvo/FPP1XSFShXo7ARvro9lul4AJlkNN 35 | VgX0gbWtkiAx0uLqlbMsC6imj2L9boRse7mzI/Ymem5SNTn9GUnlMiZ74rca9UT4 36 | Dk9YytrT4FSpomiL6z8Xj604W3RuLSdEfpfcn3Jh2tFSZ9hyLwB7ATUTA/yuj1SU 37 | G1gmoPMvlnPzNj2lIqyIdQxGdxt+L3mFO20CxBkeieWqQuNptpjwptliFjkZJJZP 38 | wQlx9qLLvs/eFC2AUWj+hbsl37PuARR9hoeqbKRcUjwGtaXOqikrvX1qzPc2+ij9 39 | /w== 40 | -----END CERTIFICATE----- 41 | -------------------------------------------------------------------------------- /src/main/java/sample/stream/WritePrimes.java: -------------------------------------------------------------------------------- 1 | package sample.stream; 2 | 3 | import akka.Done; 4 | import akka.NotUsed; 5 | import akka.actor.ActorSystem; 6 | import akka.stream.*; 7 | import akka.stream.javadsl.*; 8 | import akka.util.ByteString; 9 | import scala.concurrent.forkjoin.ThreadLocalRandom; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.util.Iterator; 14 | import java.util.concurrent.CompletionStage; 15 | 16 | public class WritePrimes { 17 | public static void main(String[] args) throws IOException { 18 | final ActorSystem system = ActorSystem.create("Sys"); 19 | final ActorMaterializer materializer = ActorMaterializer.create(system); 20 | 21 | // generate random numbers 22 | final int maxRandomNumberSize = 1000000; 23 | final Source primeSource = Source.from(new RandomIterable(maxRandomNumberSize)). 24 | // filter prime numbers 25 | filter(WritePrimes::isPrime). 26 | // and neighbor +2 is also prime 27 | filter(prime -> isPrime(prime + 2)); 28 | 29 | // write to file sink 30 | Sink> output = FileIO.toFile(new File("target/primes.txt")); 31 | Sink> slowSink = 32 | Flow.of(Integer.class) 33 | .map(i -> { 34 | // simulate slow consumer 35 | Thread.sleep(1000); 36 | return ByteString.fromString(i.toString()); 37 | }).toMat(output, Keep.right()); 38 | 39 | // console output sink 40 | Sink> consoleSink = Sink.foreach(System.out::println); 41 | 42 | // connect the graph, materialize and retrieve the completion Future 43 | final Graph> graph = GraphDSL.create(slowSink, (b, sink) -> { 44 | final UniformFanOutShape bcast = b.add(Broadcast. create(2)); 45 | b.from(b.add(primeSource)).viaFanOut(bcast).to(sink) 46 | .from(bcast).to(b.add(consoleSink)); 47 | return ClosedShape.getInstance(); 48 | }); 49 | final CompletionStage future = RunnableGraph.fromGraph(graph).run(materializer); 50 | 51 | future.handle((ioResult, failure) -> { 52 | if (failure != null) System.err.println("Failure: " + failure); 53 | else if (!ioResult.wasSuccessful()) System.err.println("Writing to file failed " + ioResult.getError()); 54 | else System.out.println("Successfully wrote " + ioResult.getCount() + " bytes"); 55 | system.terminate(); 56 | return NotUsed.getInstance(); 57 | }); 58 | } 59 | 60 | private static boolean isPrime(int n) { 61 | if (n <= 1) 62 | return false; 63 | else if (n == 2) 64 | return true; 65 | else { 66 | for (int i = 2; i < n; i++) { 67 | if (n % i == 0) 68 | return false; 69 | } 70 | return true; 71 | } 72 | } 73 | } 74 | 75 | class RandomIterable implements Iterable { 76 | 77 | private final int maxRandomNumberSize; 78 | 79 | RandomIterable(int maxRandomNumberSize) { 80 | this.maxRandomNumberSize = maxRandomNumberSize; 81 | } 82 | 83 | @Override 84 | public Iterator iterator() { 85 | return new Iterator() { 86 | @Override 87 | public boolean hasNext() { 88 | return true; 89 | } 90 | 91 | @Override 92 | public Integer next() { 93 | return ThreadLocalRandom.current().nextInt(maxRandomNumberSize); 94 | } 95 | }; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/sample/stream/TcpEcho.java: -------------------------------------------------------------------------------- 1 | package sample.stream; 2 | 3 | import akka.Done; 4 | import akka.NotUsed; 5 | import akka.actor.ActorSystem; 6 | import akka.dispatch.OnComplete; 7 | import akka.dispatch.OnFailure; 8 | import akka.dispatch.OnSuccess; 9 | import akka.stream.ActorMaterializer; 10 | import akka.stream.javadsl.Flow; 11 | import akka.stream.javadsl.Sink; 12 | import akka.stream.javadsl.Source; 13 | import akka.stream.javadsl.Tcp; 14 | import akka.stream.javadsl.Tcp.IncomingConnection; 15 | import akka.stream.javadsl.Tcp.ServerBinding; 16 | import akka.util.ByteString; 17 | import scala.concurrent.Future; 18 | import scala.runtime.BoxedUnit; 19 | 20 | import java.io.IOException; 21 | import java.net.InetSocketAddress; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import java.util.concurrent.CompletionStage; 25 | 26 | public class TcpEcho { 27 | 28 | /** 29 | * Use without parameters to start both client and server. 30 | * 31 | * Use parameters `server 0.0.0.0 6001` to start server listening on port 32 | * 6001. 33 | * 34 | * Use parameters `client 127.0.0.1 6001` to start client connecting to server 35 | * on 127.0.0.1:6001. 36 | * 37 | */ 38 | public static void main(String[] args) throws IOException { 39 | if (args.length == 0) { 40 | ActorSystem system = ActorSystem.create("ClientAndServer"); 41 | InetSocketAddress serverAddress = new InetSocketAddress("127.0.0.1", 6000); 42 | server(system, serverAddress); 43 | client(system, serverAddress); 44 | } else { 45 | InetSocketAddress serverAddress; 46 | if (args.length == 3) { 47 | serverAddress = new InetSocketAddress(args[1], Integer.valueOf(args[2])); 48 | } else { 49 | serverAddress = new InetSocketAddress("127.0.0.1", 6000); 50 | } 51 | if (args[0].equals("server")) { 52 | ActorSystem system = ActorSystem.create("Server"); 53 | server(system, serverAddress); 54 | } else if (args[0].equals("client")) { 55 | ActorSystem system = ActorSystem.create("Client"); 56 | client(system, serverAddress); 57 | } 58 | } 59 | } 60 | 61 | public static void server(ActorSystem system, InetSocketAddress serverAddress) { 62 | final ActorMaterializer materializer = ActorMaterializer.create(system); 63 | 64 | final Sink> handler = Sink.foreach(conn -> { 65 | System.out.println("Client connected from: " + conn.remoteAddress()); 66 | conn.handleWith(Flow.create(), materializer); 67 | }); 68 | 69 | 70 | final CompletionStage bindingFuture = 71 | Tcp.get(system).bind(serverAddress.getHostString(), serverAddress.getPort()).to(handler).run(materializer); 72 | 73 | bindingFuture.handle((ServerBinding binding, Throwable exception) -> { 74 | if (binding != null) { 75 | System.out.println("Server started, listening on: " + binding.localAddress()); 76 | } else { 77 | System.err.println("Server could not bind to " + serverAddress + " : " + exception.getMessage()); 78 | system.shutdown(); 79 | } 80 | return NotUsed.getInstance(); 81 | }); 82 | 83 | } 84 | 85 | public static void client(ActorSystem system, InetSocketAddress serverAddress) { 86 | final ActorMaterializer materializer = ActorMaterializer.create(system); 87 | 88 | final List testInput = new ArrayList<>(); 89 | for (char c = 'a'; c <= 'z'; c++) { 90 | testInput.add(ByteString.fromString(String.valueOf(c))); 91 | } 92 | 93 | Source responseStream = 94 | Source.from(testInput).via(Tcp.get(system).outgoingConnection(serverAddress.getHostString(), serverAddress.getPort())); 95 | 96 | CompletionStage result = responseStream.runFold( 97 | ByteString.empty(), (acc, in) -> acc.concat(in), materializer); 98 | 99 | result.handle((success, failure) -> { 100 | if (failure != null) { 101 | System.err.println("Failure: " + failure.getMessage()); 102 | } else { 103 | System.out.println("Result: " + success.utf8String()); 104 | } 105 | System.out.println("Shutting down client"); 106 | system.shutdown(); 107 | return NotUsed.getInstance(); 108 | }); 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. -------------------------------------------------------------------------------- /src/main/java/sample/stream/TcpTLSEcho.java: -------------------------------------------------------------------------------- 1 | package sample.stream; 2 | 3 | import akka.Done; 4 | import akka.NotUsed; 5 | import akka.actor.ActorSystem; 6 | import akka.japi.pf.PFBuilder; 7 | import akka.stream.ActorMaterializer; 8 | import akka.stream.TLSClientAuth; 9 | import akka.stream.TLSProtocol; 10 | import akka.stream.TLSRole; 11 | import akka.stream.javadsl.*; 12 | import akka.stream.javadsl.Tcp.IncomingConnection; 13 | import akka.stream.javadsl.Tcp.ServerBinding; 14 | import akka.util.ByteString; 15 | import com.typesafe.sslconfig.akka.AkkaSSLConfig; 16 | import com.typesafe.sslconfig.ssl.ClientAuth; 17 | import com.typesafe.sslconfig.ssl.KeyManagerFactoryWrapper; 18 | import com.typesafe.sslconfig.ssl.SSLConfigSettings; 19 | import scala.PartialFunction; 20 | 21 | import javax.net.ssl.SSLContext; 22 | import javax.net.ssl.SSLParameters; 23 | import javax.net.ssl.TrustManagerFactory; 24 | import java.io.InputStream; 25 | import java.net.InetSocketAddress; 26 | import java.security.KeyStore; 27 | import java.security.SecureRandom; 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | import java.util.Optional; 31 | import java.util.concurrent.CompletionStage; 32 | 33 | public class TcpTLSEcho { 34 | 35 | /** 36 | * Use without parameters to start both client and server. 37 | *

38 | * Use parameters `server 0.0.0.0 6001` to start server listening on port 39 | * 6001. 40 | *

41 | * Use parameters `client 127.0.0.1 6001` to start client connecting to server 42 | * on 127.0.0.1:6001. 43 | */ 44 | public static void main(String[] args) throws Exception { 45 | if (args.length == 0) { 46 | ActorSystem serverSystem = ActorSystem.create("Server"); 47 | ActorSystem clientSystem = ActorSystem.create("Client"); 48 | InetSocketAddress serverAddress = new InetSocketAddress("127.0.0.1", 6000); 49 | 50 | server(serverSystem, serverAddress); 51 | 52 | // http://typesafehub.github.io/ssl-config/CertificateGeneration.html#client-configuration 53 | client(clientSystem, serverAddress); 54 | 55 | } else { 56 | InetSocketAddress serverAddress; 57 | if (args.length == 3) { 58 | serverAddress = new InetSocketAddress(args[1], Integer.valueOf(args[2])); 59 | } else { 60 | serverAddress = new InetSocketAddress("127.0.0.1", 6000); 61 | } 62 | if (args[0].equals("server")) { 63 | ActorSystem system = ActorSystem.create("Server"); 64 | server(system, serverAddress); 65 | } else if (args[0].equals("client")) { 66 | ActorSystem system = ActorSystem.create("Client"); 67 | client(system, serverAddress); 68 | } 69 | } 70 | } 71 | 72 | // ------------------------ server ------------------------------ 73 | 74 | public static void server(ActorSystem system, InetSocketAddress serverAddress) { 75 | final ActorMaterializer materializer = ActorMaterializer.create(system); 76 | 77 | final Sink> handler = Sink.foreach((IncomingConnection conn) -> { 78 | system.log().info("Client connected from: " + conn.remoteAddress()); 79 | 80 | final Flow h = 81 | Flow.create() 82 | // .log("Server raw incoming bytes") // uncomment to see encrypted raw bytes 83 | .via(tlsStage(system, TLSRole.server()).reversed() 84 | .join( 85 | Flow.create().log("in the server handler", bs -> bs.utf8String()) 86 | .takeWhile(p -> p.utf8String().endsWith("z"), true) // close connection once "z" received (last letter we expect) 87 | )); 88 | 89 | conn.handleWith(h, materializer); 90 | }); 91 | 92 | 93 | final CompletionStage bindingFuture = 94 | Tcp.get(system).bind(serverAddress.getHostString(), serverAddress.getPort()).to(handler).run(materializer); 95 | 96 | bindingFuture.handle((ServerBinding binding, Throwable exception) -> { 97 | if (binding != null) { 98 | system.log().info("Server started, listening on: " + binding.localAddress()); 99 | } else { 100 | system.log().info("Server could not bind to " + serverAddress + " : " + exception.getMessage()); 101 | //system.shutdown(); 102 | } 103 | return NotUsed.getInstance(); 104 | }); 105 | 106 | } 107 | 108 | // ------------------------ end of server ------------------------------ 109 | 110 | 111 | // ------------------------ client ------------------------------ 112 | 113 | public static void client(ActorSystem system, InetSocketAddress serverAddress) throws Exception { 114 | final ActorMaterializer materializer = ActorMaterializer.create(system); 115 | 116 | final List testInput = new ArrayList<>(); 117 | for (char c = 'a'; c <= 'z'; c++) { 118 | testInput.add(ByteString.fromString(String.valueOf(c))); 119 | } 120 | 121 | final Sink> sink = Sink.fold( 122 | ByteString.empty(), (acc, in) -> acc.concat(in)); 123 | 124 | final Source source = Source.from(testInput).log("Element out", bs -> bs.utf8String()); 125 | 126 | final Flow> connection = 127 | Tcp.get(system).outgoingConnection(serverAddress.getHostString(), serverAddress.getPort()); 128 | 129 | final CompletionStage result = Flow.fromSinkAndSourceMat(sink, source, Keep.left()) 130 | .joinMat(tlsStage(system, TLSRole.client()), Keep.left()) 131 | .joinMat(Flow.create().via(connection), Keep.left()) 132 | .run(materializer); 133 | 134 | result.handle((success, failure) -> { 135 | if (failure != null) { 136 | system.log().info("Failure: " + failure.getMessage()); 137 | } else { 138 | system.log().info("Result: " + success.utf8String()); 139 | } 140 | system.log().info("Shutting down client"); 141 | system.terminate(); 142 | return NotUsed.getInstance(); 143 | }); 144 | } 145 | 146 | // ------------------------ end of client ------------------------------ 147 | 148 | 149 | // ------------------------ setting up TLS ------------------------ 150 | 151 | @SuppressWarnings("unchecked") 152 | private static BidiFlow tlsStage(ActorSystem system, TLSRole role) throws Exception { 153 | final AkkaSSLConfig sslConfig = AkkaSSLConfig.get(system); 154 | 155 | final SSLConfigSettings config = sslConfig.config(); 156 | 157 | // ----------- 158 | final char[] password = "abcdef".toCharArray();// do not store passwords in code, read them from somewhere safe! 159 | 160 | final KeyStore ks = KeyStore.getInstance("PKCS12"); 161 | final InputStream keystore = TcpTLSEcho.class.getClassLoader().getResourceAsStream("keys/server.p12"); 162 | 163 | ks.load(keystore, password); 164 | 165 | 166 | // initial ssl context 167 | final TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); 168 | tmf.init(ks); 169 | 170 | final KeyManagerFactoryWrapper keyManagerFactory = sslConfig.buildKeyManagerFactory(config); 171 | keyManagerFactory.init(ks, password); 172 | 173 | final SSLContext sslContext = SSLContext.getInstance("TLS"); 174 | sslContext.init(keyManagerFactory.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom()); 175 | 176 | // protocols 177 | final SSLParameters defaultParams = sslContext.getDefaultSSLParameters(); 178 | final String[] defaultProtocols = defaultParams.getProtocols(); 179 | final String[] protocols = sslConfig.configureProtocols(defaultProtocols, config); 180 | defaultParams.setProtocols(protocols); 181 | 182 | // ciphers 183 | final String[] defaultCiphers = defaultParams.getCipherSuites(); 184 | final String[] cipherSuites = sslConfig.configureCipherSuites(defaultCiphers, config); 185 | defaultParams.setCipherSuites(cipherSuites); 186 | 187 | TLSProtocol.NegotiateNewSession firstSession = 188 | TLSProtocol.negotiateNewSession() 189 | .withCipherSuites(cipherSuites) 190 | .withProtocols(protocols) 191 | .withParameters(defaultParams); 192 | 193 | // auth 194 | 195 | final Optional clientAuth = getClientAuth(config.sslParametersConfig().clientAuth()); 196 | if (clientAuth.isPresent()) { 197 | firstSession = firstSession.withClientAuth(clientAuth.get()); 198 | } 199 | 200 | final BidiFlow tls = 201 | TLS.create(sslContext, firstSession, role); 202 | 203 | final PartialFunction pf = 204 | new PFBuilder() 205 | .match(TLSProtocol.SessionBytes.class, (sb) -> ((TLSProtocol.SessionBytes) sb).bytes()) 206 | .match(TLSProtocol.SslTlsInbound.class, (ib) -> { 207 | system.log().info("Received other that SessionBytes" + ib); 208 | return null; 209 | }) 210 | .build(); 211 | 212 | final BidiFlow tlsSupport = 213 | BidiFlow.fromFlows( 214 | Flow.create().map(TLSProtocol.SendBytes::new), 215 | Flow.create().collect(pf)); 216 | 217 | return tlsSupport.atop(tls); 218 | } 219 | 220 | private static Optional getClientAuth(ClientAuth auth) { 221 | if (auth.equals(ClientAuth.want())) { 222 | return Optional.of(TLSClientAuth.want()); 223 | } else if (auth.equals(ClientAuth.need())) { 224 | return Optional.of(TLSClientAuth.need()); 225 | } else if (auth.equals(ClientAuth.none())) { 226 | return Optional.of(TLSClientAuth.none()); 227 | } else { 228 | return Optional.empty(); 229 | } 230 | } 231 | 232 | } 233 | -------------------------------------------------------------------------------- /tutorial/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Demonstrating Akka Streams 4 | 5 | 6 | 7 |

8 |

9 | This tutorial contains a few samples that demonstrates Akka Streams. 10 |

11 | 12 |

13 | Akka Streams is an implementation of 14 | Reactive Streams, 15 | which is a standard for asynchronous stream processing with non-blocking backpressure. 16 | Akka Streams is interoperable with other Reactive Streams implementations. 17 |

18 | 19 |

20 | Akka Streams is currently under development and these samples use a preview release, i.e. 21 | changes can be expected. Please try it out and send feedback to the 22 | Akka mailing list. 23 |

24 | 25 |

26 | Akka Streams provides a way to express and run a chain of asynchronous processing 27 | steps acting on a sequence of elements. Every step is processed by one actor to 28 | support parallelism. The user describes the “what” instead of the “how”, i.e. things like 29 | batching, buffering, thread-safety are handled behind the scenes. 30 |

31 | 32 |

33 | The processing steps are declared with a DSL, a so called Flow. 34 | A Flow may be connected to a Source and/or a 35 | Sink. It may also exist without either of these end points, as an 36 | "open" flow. Any open flow when connected to a Source itself becomes a 37 | Source and likewise when connected to a Sink becomes a 38 | Sink. A Flow with both a Source and a 39 | Sink is called a RunnableFlow and may be executed. 40 |

41 |

42 | The Source can be constructed from a collection, an iterator, 43 | a future, or a function which is evaluated repeatedly. 44 |

45 | 46 |

47 | Each DSL element produces a new Flow that can be further transformed, building 48 | up a description of the complete transformation pipeline. In order to execute 49 | this pipeline the Flow must be runnable (have both Source 50 | and Sink endpoints, and is materialized by calling one of the execution 51 | methods which include .run, .runWith and .runForeach. 52 |

53 | 54 |

55 | Running a Flow involves a process called materialization, which requires 56 | a FlowMaterializer configured for an actor system. 57 |

58 | 59 |

60 | It should be noted that the streams modeled by this library are “hot”, 61 | meaning that they asynchronously flow through a series of processors without 62 | detailed control by the user. In particular it is not predictable how many 63 | elements a given transformation step might buffer before handing elements 64 | downstream, which means that transformation functions may be invoked more 65 | often than for corresponding transformations on strict collections like 66 | List. An important consequence is that elements that were 67 | produced into a stream may be discarded by later processors, e.g. when using the 68 | take combinator. 69 |

70 | 71 |

72 | By default every operation is executed within its own Actor 73 | to enable full pipelining of the chained set of computations. This behavior 74 | is determined by the akka.stream.ActorMaterializer which is required 75 | by those methods that materialize the Flow into a series of 76 | org.reactivestreams.Processor instances that are started and active. 77 | Synchronous compaction of steps is possible (but not yet implemented). 78 |

79 | 80 |
81 |
82 |

Basic transformation

83 | 84 |

85 | What does a Flow look like? 86 |

87 | 88 |

89 | Open BasicTransformation.java 90 |

91 | 92 |

93 | Here we use an Iterator over the Array produced by splitting the text 94 | using the spaces, as input producer; 95 | note that the iterator is an Iterator<String> and this produces 96 | a Source<String>. The flow written to use this Source 97 | must match the type, so we could not treat the source as a source of Int 98 | for example. 99 |

100 | 101 |

102 | In this sample we convert each read line to upper case and printing it 103 | to the console. This is done in the lines map(e -> e.toUpperCase) and 104 | runForeach(System.out::println, materializer). 105 |

106 | 107 |

108 | The map(e -> e.toUpperCase) takes Strings and produces 109 | Strings. Behind the scenes, this constructs a Transformer<String, String> 110 | which is itself a Flow 111 | When this is attached to the Source<String>, the result is 112 | a new Flow that is also a Source<String>. If the map 113 | was over a function that converted, say, String to Int, the result would be a 114 | Source<Int> when attaching it to this Source<String>. 115 |

116 | 117 |

118 | The runForeach(System.out::println, materializer) constructs and attaches a Sink, in 119 | this case an implementation called Sink.runForeach and again this is 120 | specifically a Sink.runForeach<String> which matches the type of the 121 | Source<String>. The result of attaching this matching Sink 122 | to the Source creates a RunnableFlow which is then also run 123 | bu the runForeach call. 124 |

125 | 126 |

Unlike a runForeach on a collection (which returns Unit), 127 | the runForeach on a Flow returns a CompletionStage<Done> instead. Because 128 | we get a CompletionStage back, we can use it to shutdown the actor system once the 129 | flow is completed. This is accomplished by the final line in the flow: 130 |


131 | handle((done, failure) -> {
132 |   system.terminate();
133 |   return NotUsed.getInstance();
134 | });
135 | 
136 | 137 |

138 | Try to run the sample.stream.BasicTransformation class 139 | by selecting it in the 'Main class' menu in the Run tab 140 | and click the 'Run' button. 141 |

142 | 143 |

144 | Try to add additional steps in the flow, for example skip short lines: 145 |

146 | 147 |

148 | filter(line -> line.length > 3).
149 | 
150 | 151 |

The API is intended to be familiar to anyone used to the collections API in Scala.

152 | 153 |

154 | All stream manipulation operations can be found in the 155 | API documentation. 156 |

157 | 158 |
159 |
160 |

Backpressure

161 | 162 |

163 | The mandatory non-blocking backpressure is a key feature of 164 | Reactive Streams. 165 |

166 | 167 |

168 | Open WritePrimes.java 169 |

170 | 171 |

172 | In this sample we use a fast producer and several consumers, with potentially different throughput capacity. 173 | To avoid out of memory problems it is important that the producer does not generate elements faster than 174 | what can be consumed. Also the speed of the slowest consumer must be taken into account to avoid 175 | unbounded buffering in intermediate steps. 176 |

177 | 178 |

179 | Here we use a random number generator as input. The input producer is a block of code which is 180 | evaluated repeatedly. It can generate elements very fast if needed. 181 |

182 | 183 |

184 | We filter the numbers through two prime number checks and end up with a stream of 185 | prime numbers, which neighbor +2 number is also a prime number. These two flow filter steps 186 | can potentially be pipelined, i.e. executed in parallel. 187 |

188 | 189 |

190 | Then we connect that prime number producer to two consumers. One writing to a file, and another 191 | printing to the console. To simulate that the file writer is slow we have added an additional 192 | sleep in a map stage right before the SynchronousFileSink. 193 |

194 | 195 |

196 | The connections are made using the FlowGraph DSL: we use the FlowGraph.factory().closed(...) 197 | method to construct a runnable graph (substituting 'partial' for 'closed' would create one that has open 198 | inputs or outputs that remain to be connected). The first argument is the 'runForeach' Sink that materializes 199 | to a Future that we can use to detect (abnormal) stream termination, the second argument is 200 | a lambda expression that takes a FlowGraph.Builder and the imported 'slowSink' shape and 201 | performs the actual wiring. 202 |

203 | 204 |

205 | The first step is to create and import a broadcast node with two outputs, then we use the builder to 206 | connect the source via the broadcast to both sinks. 207 |

208 | 209 |

210 | Try to run the sample.stream.WritePrimes class 211 | by selecting it in the 'Main class' menu in the Run tab 212 | and click the 'Run' button. 213 |

214 | 215 |

216 | Note that speed of the output in the console is limited by the slow file writer, i.e. 217 | one element per second. 218 |

219 | 220 |

221 | Open primes.txt to see 222 | the file output. 223 |

224 | 225 |
226 |
227 |

Stream of streams

228 | 229 |

230 | Let us take a look at an example of more advanced stream manipulation. 231 |

232 | 233 |

234 | Open GroupLogFile.java 235 |

236 | 237 |

238 | We want to read a log file and pipe entries of different log level to separate files. 239 |

240 | 241 |

242 | In this sample we exctract the level with a regular expression matching the log levels and then 243 | write the elements of each group to a separate file. 244 |

245 | 246 |

247 | Try to run the sample.stream.GroupLogFile class 248 | by selecting it in the 'Main class' menu in the Run tab 249 | and click the 'Run' button. 250 |

251 | 252 |

253 | Open the input logfile.txt 254 | and look at the resulting output log files in the 255 | target directory. 256 |

257 | 258 |
259 |
260 |

TCP Stream

261 | 262 |

263 | Akka Streams also provides a stream based API on top of 264 | Akka I/O. 265 |

266 | 267 |

268 | Open TcpEcho.java 269 |

270 | 271 |

272 | When you Run TcpEcho without parameters it starts 273 | both client and server in the same JVM and the client connects to the server over port 6000. 274 |

275 | 276 |

277 | The server is started by calling bind on the akka.stream.javadsl.Tcp extension. 278 | It returns a StreamTcp.ServerBinding. 279 | Each new client connection is represented by a new IncomingTcpConnection 280 | element produced by the connections Source<StreamTcp.IncomingConnection> of the 281 | ServerBinding. From the connection the server can operate on the ByteString 282 | elements. 283 |

284 | 285 |

286 | In this sample the server sends back the same bytes as it receives. 287 |

288 |

289 | conn.handleWith(Flow.empty(), materializer);
290 | 
291 | 292 |

293 | You can add transformation of the bytes using a Flow. For example convert characters to 294 | upper case. 295 |

296 |

297 | final Flow<ByteString, ByteString, NotUsed> toUpper =
298 |     Flow.<ByteString>create().map(byteStr -> ByteString.fromString(byteStr.utf8String().toUpperCase()));
299 | conn.handleWith(toUpper, materializer);
300 |  
301 | 302 |

303 | The connection from the client is established by calling outgoingConnection on the 304 | akka.stream.javadsl.Tcp extension and attaching corresponding flow to input 305 | Source and output Sink. 306 |

307 | 308 |

309 | Future<ByteString> result = responseStream.runFold(
310 |   ByteString.empty(), (acc, in) -> acc.concat(in), materializer);
311 | 
312 | 313 |

314 | In this sample the client sends a sequence of characters one-by-one to the server, aggregates the replies 315 | into a single ByteString, and finally prints that. 316 |

317 | 318 |

319 | Try to run the sample.stream.TcpEcho class 320 | by selecting it in the 'Main class' menu in the Run tab 321 | and click the 'Run' button. 322 |

323 | 324 |

325 | That runs the client and server in the same JVM process. It can be more 326 | interesting to run them in separate processes. Run the following commands in separate 327 | terminal windows. 328 |

329 | 330 |

331 | <path to activator dir>/activator "run-main sample.stream.TcpEcho server 0.0.0.0 6001"
332 | 
333 | 334 |

335 | <path to activator dir>/activator "run-main sample.stream.TcpEcho client 127.0.0.1 6001"
336 | 
337 | 338 |

339 | You can also interact with the server with telnet: 340 |

341 | 342 |

343 | telnet 127.0.0.1 6001
344 | 
345 | 346 |

347 | Type a few characters in the telnet session and press enter to see them echoed back to the terminal. 348 |

349 | 350 |
351 |
352 |

Links

353 | 354 | 359 | 360 |
361 | 362 | 363 | 364 | 365 | --------------------------------------------------------------------------------