├── RxAndroidTest ├── app │ ├── .gitignore │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── values │ │ │ │ │ ├── colors.xml │ │ │ │ │ ├── dimens.xml │ │ │ │ │ ├── styles.xml │ │ │ │ │ └── strings.xml │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ ├── ic_launcher.xml │ │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── drawable-v24 │ │ │ │ │ └── ic_launcher_foreground.xml │ │ │ │ ├── layout │ │ │ │ │ └── activity_login.xml │ │ │ │ └── drawable │ │ │ │ │ └── ic_launcher_background.xml │ │ │ └── AndroidManifest.xml │ │ ├── test │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── adamldavis │ │ │ │ └── rxadroidtest │ │ │ │ └── ExampleUnitTest.java │ │ └── androidTest │ │ │ └── java │ │ │ └── com │ │ │ └── adamldavis │ │ │ └── rxadroidtest │ │ │ └── ExampleInstrumentedTest.java │ ├── proguard-rules.pro │ └── build.gradle ├── settings.gradle ├── README.md ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── build.gradle ├── gradle.properties ├── .gitignore ├── gradlew.bat ├── gradlew └── LICENSE ├── reactive-streams-in-java ├── gradle.properties ├── testfile.txt ├── compare_performance.ods ├── README.md ├── .gitignore ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── adamldavis │ │ │ ├── ReactiveStreamsDemo.java │ │ │ ├── MessageConsumer.java │ │ │ ├── MessageController.java │ │ │ ├── Application.java │ │ │ ├── Channel.java │ │ │ ├── AkkaStreamsDemo.java │ │ │ ├── ReactorDemo.java │ │ │ └── RxJavaDemo.java │ └── test │ │ └── java │ │ └── com │ │ └── github │ │ └── adamldavis │ │ ├── DemoData.java │ │ ├── RxJavaDemoTest.java │ │ ├── ReactorDemo10Test.java │ │ ├── AkkaStreamsDemoTest.java │ │ ├── ReactorDemoTest.java │ │ └── PerformanceTest.java ├── build.gradle ├── perf.csv ├── pom.xml └── LICENSE ├── humblecode ├── settings.gradle ├── mongo.sh ├── src │ ├── main │ │ ├── resources │ │ │ ├── static │ │ │ │ ├── images │ │ │ │ │ ├── Holly.gif │ │ │ │ │ ├── Olive.gif │ │ │ │ │ ├── Zamia.gif │ │ │ │ │ ├── Eggplant.gif │ │ │ │ │ ├── Pinkroot.gif │ │ │ │ │ ├── Sugar_Cane.gif │ │ │ │ │ └── Honeysuckle.gif │ │ │ │ ├── error │ │ │ │ │ └── 404.html │ │ │ │ ├── css │ │ │ │ │ ├── prettify.css │ │ │ │ │ └── base.css │ │ │ │ └── js │ │ │ │ │ ├── html5shiv.min.js │ │ │ │ │ └── main.js │ │ │ └── templates │ │ │ │ ├── pay.ftl │ │ │ │ ├── login.ftl │ │ │ │ ├── home.ftl │ │ │ │ └── account.ftl │ │ └── java │ │ │ └── com │ │ │ └── humblecode │ │ │ └── humblecode │ │ │ ├── model │ │ │ ├── TestResult.java │ │ │ ├── Payment.java │ │ │ ├── Question.java │ │ │ ├── Segment.java │ │ │ ├── Test.java │ │ │ ├── Course.java │ │ │ └── User.java │ │ │ ├── HumblecodeApplication.java │ │ │ ├── WebFluxConfig.java │ │ │ ├── rest │ │ │ ├── PaymentControl.java │ │ │ └── CourseControl.java │ │ │ ├── SecurityConfig.java │ │ │ └── web │ │ │ └── WebControl.java │ └── test │ │ └── java │ │ └── com │ │ └── humblecode │ │ └── humblecode │ │ └── HumblecodeApplicationTests.java ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── README.md ├── humblecode │ └── .gitignore ├── .gitignore ├── build.gradle ├── gradlew.bat └── gradlew ├── .gitattributes ├── 9781484241752.jpg ├── akka-http-java ├── README.md ├── .gitignore ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── github │ │ │ │ └── adamldavis │ │ │ │ └── akkahttp │ │ │ │ ├── MessageRepository.java │ │ │ │ ├── ChatMessage.java │ │ │ │ ├── WebApp.java │ │ │ │ ├── JmsToWebSocket.java │ │ │ │ ├── ChatServer.java │ │ │ │ └── WebSocketExample.java │ │ └── resources │ │ │ └── akkahttp │ │ │ └── index.html │ └── test │ │ └── java │ │ └── com │ │ └── github │ │ └── adamldavis │ │ └── akkhttp │ │ ├── WebAppTest.java │ │ ├── MessageRepositoryTest.java │ │ └── ChatServerTest.java └── build.gradle ├── errata.md ├── README.md ├── Contributing.md └── LICENSE.txt /RxAndroidTest/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /reactive-streams-in-java/gradle.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /RxAndroidTest/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /reactive-streams-in-java/testfile.txt: -------------------------------------------------------------------------------- 1 | foo 2 | bar 3 | -------------------------------------------------------------------------------- /humblecode/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'humblecode' 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /9781484241752.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/9781484241752.jpg -------------------------------------------------------------------------------- /RxAndroidTest/README.md: -------------------------------------------------------------------------------- 1 | # RxAndroidTest 2 | Demo of RxJava, RxAndroid, RxLifecycle, and RxBinding 3 | -------------------------------------------------------------------------------- /humblecode/mongo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mongod --dbpath data/ --fork --logpath ~/mongodb/logs/mongodb.log 4 | 5 | -------------------------------------------------------------------------------- /RxAndroidTest/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/RxAndroidTest/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /reactive-streams-in-java/compare_performance.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/reactive-streams-in-java/compare_performance.ods -------------------------------------------------------------------------------- /humblecode/src/main/resources/static/images/Holly.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/humblecode/src/main/resources/static/images/Holly.gif -------------------------------------------------------------------------------- /humblecode/src/main/resources/static/images/Olive.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/humblecode/src/main/resources/static/images/Olive.gif -------------------------------------------------------------------------------- /humblecode/src/main/resources/static/images/Zamia.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/humblecode/src/main/resources/static/images/Zamia.gif -------------------------------------------------------------------------------- /RxAndroidTest/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/RxAndroidTest/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /RxAndroidTest/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/RxAndroidTest/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /humblecode/src/main/resources/static/images/Eggplant.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/humblecode/src/main/resources/static/images/Eggplant.gif -------------------------------------------------------------------------------- /humblecode/src/main/resources/static/images/Pinkroot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/humblecode/src/main/resources/static/images/Pinkroot.gif -------------------------------------------------------------------------------- /humblecode/src/main/resources/static/images/Sugar_Cane.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/humblecode/src/main/resources/static/images/Sugar_Cane.gif -------------------------------------------------------------------------------- /RxAndroidTest/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/RxAndroidTest/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /RxAndroidTest/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/RxAndroidTest/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /humblecode/src/main/resources/static/images/Honeysuckle.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/humblecode/src/main/resources/static/images/Honeysuckle.gif -------------------------------------------------------------------------------- /RxAndroidTest/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/RxAndroidTest/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /RxAndroidTest/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/RxAndroidTest/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /RxAndroidTest/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/RxAndroidTest/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /RxAndroidTest/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/RxAndroidTest/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /RxAndroidTest/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/RxAndroidTest/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /RxAndroidTest/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/reactive-streams-in-java/HEAD/RxAndroidTest/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /akka-http-java/README.md: -------------------------------------------------------------------------------- 1 | # akka-http-java 2 | 3 | ## Demo of Akka Http + Akka Streams in Java 4 | 5 | Built to accompany the book "Reactive Streams in Java" 6 | 7 | See [akka-http docs](https://doc.akka.io/docs/akka-http/10.1.3/introduction.html) for more. 8 | 9 | -------------------------------------------------------------------------------- /RxAndroidTest/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /RxAndroidTest/app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /errata.md: -------------------------------------------------------------------------------- 1 | # Errata for *Book Title* 2 | 3 | On **page xx** [Summary of error]: 4 | 5 | Details of error here. Highlight key pieces in **bold**. 6 | 7 | *** 8 | 9 | On **page xx** [Summary of error]: 10 | 11 | Details of error here. Highlight key pieces in **bold**. 12 | 13 | *** -------------------------------------------------------------------------------- /RxAndroidTest/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Sep 21 21:45:59 EDT 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /humblecode/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Jun 26 14:24:36 EDT 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.5.1-all.zip 7 | -------------------------------------------------------------------------------- /reactive-streams-in-java/README.md: -------------------------------------------------------------------------------- 1 | # reactive-streams-in-java 2 | Reactive Streams in Java 3 | 4 | Examples of three different implementations of reactive streams in Java. 5 | - Akka Stream 6 | - Project Reactor 7 | - RxJava2 8 | 9 | ## Building 10 | 11 | Can be built with either Gradle or Maven. 12 | -------------------------------------------------------------------------------- /RxAndroidTest/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /RxAndroidTest/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /humblecode/src/main/resources/static/error/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 404 Not Found 6 | 7 | 8 | 9 |

Can't find it. Sorry

10 |

Please have this nice picture instead...

11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /humblecode/README.md: -------------------------------------------------------------------------------- 1 | # Demo of Spring Boot + Reactor + MongoDB 2 | 3 | Welcome to HumbleCode, my demo code for Spring Boot, Reactor, and reactive-mongo. 4 | 5 | This code is part of the leanpub book, [Reactive Streams in Java](https://leanpub.com/reactivestreamsinjava). 6 | 7 | The example is built around an online Course catalog and includes security, WebFlux configuration, 8 | and even some payment logic. 9 | -------------------------------------------------------------------------------- /humblecode/src/main/java/com/humblecode/humblecode/model/TestResult.java: -------------------------------------------------------------------------------- 1 | package com.humblecode.humblecode.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | import java.util.Date; 7 | import java.util.UUID; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | public class TestResult { 12 | 13 | UUID testId; 14 | Date date; 15 | boolean passed; 16 | float percent; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /humblecode/humblecode/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | .sts4-cache 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | /out/ 20 | 21 | ### NetBeans ### 22 | /nbproject/private/ 23 | /build/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ -------------------------------------------------------------------------------- /humblecode/src/main/resources/templates/pay.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 13 |
14 | -------------------------------------------------------------------------------- /humblecode/src/main/java/com/humblecode/humblecode/model/Payment.java: -------------------------------------------------------------------------------- 1 | package com.humblecode.humblecode.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import org.springframework.data.mongodb.core.mapping.Document; 6 | 7 | import java.util.Date; 8 | import java.util.UUID; 9 | 10 | @Document 11 | @Data 12 | @AllArgsConstructor 13 | public class Payment { 14 | 15 | Date date; 16 | UUID userId; 17 | int amount; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /RxAndroidTest/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /akka-http-java/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | # IDEA 4 | bin/ 5 | target/ 6 | build/ 7 | .idea 8 | *.iml 9 | *.ipr 10 | *.ipw 11 | 12 | #Gradle 13 | .gradle 14 | 15 | # Log file 16 | *.log 17 | 18 | # BlueJ files 19 | *.ctxt 20 | 21 | # Mobile Tools for Java (J2ME) 22 | .mtj.tmp/ 23 | 24 | # Package Files # 25 | *.jar 26 | *.war 27 | *.nar 28 | *.ear 29 | *.zip 30 | *.tar.gz 31 | *.rar 32 | 33 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 34 | hs_err_pid* 35 | -------------------------------------------------------------------------------- /RxAndroidTest/app/src/test/java/com/adamldavis/rxadroidtest/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.adamldavis.rxadroidtest; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /humblecode/src/main/java/com/humblecode/humblecode/model/Question.java: -------------------------------------------------------------------------------- 1 | package com.humblecode.humblecode.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | public class Question { 12 | 13 | String text = ""; 14 | 15 | String tip = ""; 16 | 17 | String answer = ""; 18 | 19 | List answers = new ArrayList<>(); 20 | 21 | List correctAnswers = new ArrayList<>(); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /reactive-streams-in-java/.gitignore: -------------------------------------------------------------------------------- 1 | #Gradle 2 | .gradle 3 | build/ 4 | #Eclipse 5 | .settings 6 | .classpath 7 | .project 8 | #IDEA 9 | *.iws 10 | *.iml 11 | .idea 12 | *.ipr 13 | 14 | # Compiled class file 15 | *.class 16 | 17 | # Log file 18 | *.log 19 | 20 | # BlueJ files 21 | *.ctxt 22 | 23 | # Mobile Tools for Java (J2ME) 24 | .mtj.tmp/ 25 | 26 | # Package Files # 27 | *.jar 28 | *.war 29 | *.nar 30 | *.ear 31 | *.zip 32 | *.tar.gz 33 | *.rar 34 | 35 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 36 | hs_err_pid* 37 | /bin/ 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apress Source Code 2 | 3 | This repository accompanies [*Reactive Streams in Java*](https://www.apress.com/9781484241752) by Adam L. Davis (Apress, 2019). 4 | 5 | [comment]: #cover 6 | ![Cover image](9781484241752.jpg) 7 | 8 | Download the files as a zip using the green button, or clone the repository to your machine using Git. 9 | 10 | ## Releases 11 | 12 | Release v1.0 corresponds to the code in the published book, without corrections or updates. 13 | 14 | ## Contributions 15 | 16 | See the file Contributing.md for more information on how you can contribute to this repository. -------------------------------------------------------------------------------- /humblecode/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Secret properties 5 | application.* 6 | 7 | # IDEA files 8 | .idea 9 | *.iml 10 | *.iws 11 | *.ipr 12 | out/ 13 | build/ 14 | #Gradle 15 | .gradle 16 | 17 | #mongo 18 | data/ 19 | 20 | # Log file 21 | *.log 22 | 23 | # BlueJ files 24 | *.ctxt 25 | 26 | # Mobile Tools for Java (J2ME) 27 | .mtj.tmp/ 28 | 29 | # Package Files # 30 | *.jar 31 | *.war 32 | *.nar 33 | *.ear 34 | *.zip 35 | *.tar.gz 36 | *.rar 37 | 38 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 39 | hs_err_pid* 40 | -------------------------------------------------------------------------------- /reactive-streams-in-java/src/main/java/com/github/adamldavis/ReactiveStreamsDemo.java: -------------------------------------------------------------------------------- 1 | package com.github.adamldavis; 2 | 3 | import java.util.List; 4 | import java.util.concurrent.Future; 5 | 6 | /** Interface to implement for each implementation of reactive streams in Java for performance testing. */ 7 | public interface ReactiveStreamsDemo { 8 | 9 | Future> doSquaresAsync(int count); 10 | 11 | Future doStringConcatAsync(int count); 12 | 13 | Future> doParallelSquaresAsync(int count); 14 | 15 | Future doParallelStringConcatAsync(int count); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /humblecode/src/main/resources/static/css/prettify.css: -------------------------------------------------------------------------------- 1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} 2 | -------------------------------------------------------------------------------- /RxAndroidTest/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.1.4' 11 | 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | jcenter() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /reactive-streams-in-java/src/test/java/com/github/adamldavis/DemoData.java: -------------------------------------------------------------------------------- 1 | package com.github.adamldavis; 2 | 3 | import java.util.List; 4 | 5 | import static java.util.Arrays.asList; 6 | 7 | public interface DemoData { 8 | 9 | List squares = asList(1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 10 | 144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529, 576, 11 | 625, 676, 729, 784, 841, 900, 961, 1024, 1089, 1156, 1225, 1296, 12 | 1369, 1444, 1521, 1600, 1681, 1764, 1849, 1936, 2025, 2116, 2209, 13 | 2304, 2401, 2500, 2601, 2704, 2809, 2916, 3025, 3136, 3249, 3364, 14 | 3481, 3600, 3721, 3844, 3969, 4096); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Apress Source Code 2 | 3 | Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers. 4 | 5 | ## How to Contribute 6 | 7 | 1. Make sure you have a GitHub account. 8 | 2. Fork the repository for the relevant book. 9 | 3. Create a new branch on which to make your change, e.g. 10 | `git checkout -b my_code_contribution` 11 | 4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted. 12 | 5. Submit a pull request. 13 | 14 | Thank you for your contribution! -------------------------------------------------------------------------------- /akka-http-java/src/main/java/com/github/adamldavis/akkahttp/MessageRepository.java: -------------------------------------------------------------------------------- 1 | package com.github.adamldavis.akkahttp; 2 | 3 | 4 | import java.util.concurrent.CompletableFuture; 5 | import java.util.concurrent.CompletionStage; 6 | 7 | public class MessageRepository { 8 | 9 | public CompletionStage save(ChatMessage message) { 10 | return CompletableFuture.supplyAsync(() -> { 11 | try { 12 | Thread.sleep(500); // imitate long running operation 13 | } catch (InterruptedException e) { 14 | e.printStackTrace(); 15 | } 16 | System.out.println("saving message: " + message); 17 | return message; 18 | }); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /humblecode/src/main/java/com/humblecode/humblecode/HumblecodeApplication.java: -------------------------------------------------------------------------------- 1 | package com.humblecode.humblecode; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.context.annotation.Bean; 7 | import reactor.core.publisher.Flux; 8 | 9 | @EnableAutoConfiguration 10 | @SpringBootApplication 11 | public class HumblecodeApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(HumblecodeApplication.class, args); 15 | } 16 | 17 | @Bean 18 | public Flux exampleBean() { 19 | return Flux.just("example"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /RxAndroidTest/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | -------------------------------------------------------------------------------- /humblecode/src/main/java/com/humblecode/humblecode/model/Segment.java: -------------------------------------------------------------------------------- 1 | package com.humblecode.humblecode.model; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.Objects; 6 | import java.util.UUID; 7 | 8 | @Data 9 | public class Segment { 10 | 11 | //String video; 12 | 13 | public UUID id = UUID.randomUUID(); 14 | 15 | public String name; 16 | 17 | public String body; // body of the course as text 18 | 19 | public Test test; 20 | 21 | public int hashCode() { 22 | return id == null ? 0 : id.hashCode(); 23 | } 24 | 25 | @Override 26 | public boolean equals(Object o) { 27 | if (this == o) return true; 28 | if (!(o instanceof Segment)) return false; 29 | Segment segment = (Segment) o; 30 | return Objects.equals(id, segment.id); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /RxAndroidTest/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /akka-http-java/src/test/java/com/github/adamldavis/akkhttp/WebAppTest.java: -------------------------------------------------------------------------------- 1 | package com.github.adamldavis.akkhttp; 2 | 3 | import akka.actor.ActorSystem; 4 | import akka.testkit.javadsl.TestKit; 5 | import com.github.adamldavis.akkahttp.WebApp; 6 | import org.junit.*; 7 | import static org.assertj.core.api.Assertions.assertThat; 8 | 9 | public class WebAppTest { 10 | 11 | WebApp webApp; 12 | ActorSystem actorSystem; 13 | 14 | @Before 15 | public void setup() { 16 | actorSystem = ActorSystem.create("test-system"); 17 | webApp = new WebApp(actorSystem); 18 | } 19 | @After 20 | public void tearDown() { 21 | TestKit.shutdownActorSystem(actorSystem); 22 | } 23 | 24 | @Test 25 | public void createWebsocketRoute_should_exist(){ 26 | assertThat(webApp.createWebsocketRoute()).isNotNull(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /RxAndroidTest/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | RxAdroidTest 3 | 4 | 5 | Email 6 | Password (optional) 7 | Sign in or register 8 | Sign in 9 | This email address is invalid 10 | This password is too short 11 | This password is incorrect 12 | This field is required 13 | "Contacts permissions are needed for providing email 14 | completions." 15 | 16 | 17 | -------------------------------------------------------------------------------- /humblecode/src/main/java/com/humblecode/humblecode/WebFluxConfig.java: -------------------------------------------------------------------------------- 1 | package com.humblecode.humblecode; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.reactive.config.EnableWebFlux; 5 | import org.springframework.web.reactive.config.WebFluxConfigurationSupport; 6 | import org.springframework.web.server.WebExceptionHandler; 7 | import reactor.core.publisher.Mono; 8 | 9 | @EnableWebFlux 10 | public class WebFluxConfig extends WebFluxConfigurationSupport { 11 | 12 | @Override 13 | public WebExceptionHandler responseStatusExceptionHandler() { 14 | return (exchange, ex) -> Mono.create(callback -> { 15 | exchange.getResponse().setStatusCode(HttpStatus.I_AM_A_TEAPOT); 16 | System.err.println(ex.getMessage()); 17 | callback.success(null); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /reactive-streams-in-java/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | plugins { 3 | id "java" 4 | id "application" 5 | id "idea" 6 | } 7 | 8 | mainClassName='com.github.adamldavis.Application' 9 | 10 | // REQUIRES Gradle 4.7+ and Java 10+ 11 | sourceCompatibility = 1.10 12 | targetCompatibility = 1.10 13 | 14 | repositories { 15 | mavenLocal() 16 | jcenter() 17 | } 18 | 19 | dependencies { 20 | 21 | compile("org.springframework.boot:spring-boot-starter-web:2.0.2.RELEASE") 22 | 23 | compile 'io.reactivex.rxjava2:rxjava:2.1.9' 24 | 25 | compile 'io.projectreactor:reactor-core:3.1.7.RELEASE' 26 | testCompile 'io.projectreactor:reactor-test:3.1.7.RELEASE' 27 | 28 | compile group: 'com.typesafe.akka', name: 'akka-stream_2.12', version:'2.5.12' 29 | testCompile 'com.typesafe.akka:akka-stream-testkit_2.12:2.5.12' 30 | 31 | testCompile 'junit:junit:4.12' 32 | testCompile 'org.hamcrest:hamcrest-all:1.3' 33 | } 34 | -------------------------------------------------------------------------------- /reactive-streams-in-java/src/main/java/com/github/adamldavis/MessageConsumer.java: -------------------------------------------------------------------------------- 1 | package com.github.adamldavis; 2 | 3 | import io.reactivex.functions.Consumer; 4 | 5 | /** 6 | * Implements reactive-streams Consumer interface which could be done as a Java 7 | * 8 lambda, but sometimes you might want to pull the code out into its own 8 | * class. 9 | */ 10 | public class MessageConsumer implements Consumer { 11 | 12 | final String type; 13 | 14 | public MessageConsumer(String type) { 15 | this.type = type; 16 | } 17 | 18 | @Override 19 | public void accept(String message) { 20 | // do something with the Data 21 | if (message.startsWith("Error:")) 22 | System.err.println("type=" + type + " message=" + message); 23 | else 24 | System.out.println("type=" + type + " message=" + message); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /RxAndroidTest/app/src/androidTest/java/com/adamldavis/rxadroidtest/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.adamldavis.rxadroidtest; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.adamldavis.rxadroidtest", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /humblecode/src/main/java/com/humblecode/humblecode/model/Test.java: -------------------------------------------------------------------------------- 1 | package com.humblecode.humblecode.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.UUID; 9 | 10 | /** Test user takes to complete the course. Questions are picked randomly out of given list of questions. */ 11 | @Data 12 | @AllArgsConstructor 13 | public class Test { 14 | 15 | UUID id = UUID.randomUUID(); 16 | 17 | /** List of quesions that make up the test. */ 18 | final List questions = new ArrayList<>(); 19 | 20 | /** Number of actual questions to use in the test, should be less than total number of questions. */ 21 | public int numberOfQuestions; 22 | 23 | public Test() {} 24 | 25 | public void setQuestions(List questions) { 26 | this.questions.clear(); 27 | this.questions.addAll(questions); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /reactive-streams-in-java/perf.csv: -------------------------------------------------------------------------------- 1 | PSquares akka-st, 7279337 2 | PSquares reactor, 443791 3 | PSquares rxjava, 809336 4 | PStrCon akka-st, 116305 5 | PStrCon reactor, 212981 6 | PStrCon rxjava, 303566 7 | Squares akka-st, 730614 8 | Squares reactor, 18550 9 | Squares rxjava, 161371 10 | StrCon akka-st, 164492 11 | StrCon reactor, 49480 12 | StrCon rxjava, 98594 13 | PSquares akka-st, 3551354 14 | PSquares reactor, 270502 15 | PSquares rxjava, 373837 16 | PStrCon akka-st, 72037 17 | PStrCon reactor, 83421 18 | PStrCon rxjava, 85740 19 | Squares akka-st, 113160 20 | Squares reactor, 12973 21 | Squares rxjava, 25292 22 | StrCon akka-st, 89372 23 | StrCon reactor, 15333 24 | StrCon rxjava, 8317 25 | PSquares akka-st, 2383551 26 | PSquares reactor, 192171 27 | PSquares rxjava, 304251 28 | PStrCon akka-st, 57634 29 | PStrCon reactor, 56424 30 | PStrCon rxjava, 65377 31 | Squares akka-st, 58297 32 | Squares reactor, 4306 33 | Squares rxjava, 29972 34 | StrCon akka-st, 57856 35 | StrCon reactor, 6768 36 | StrCon rxjava, 6729 37 | -------------------------------------------------------------------------------- /akka-http-java/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | apply plugin: 'java' 3 | apply plugin: 'eclipse' 4 | apply plugin: 'idea' 5 | apply plugin: 'application' 6 | 7 | group = 'com.github.adamldavis' 8 | applicationName = 'akka-http-java' 9 | version = '0.0.1-SNAPSHOT' 10 | mainClassName = 'com.github.adamldavis.akkahttp.WebApp' 11 | // requires Gradle 4.7+ 12 | sourceCompatibility = 1.10 13 | targetCompatibility = 1.10 14 | 15 | repositories { 16 | mavenCentral() 17 | } 18 | ext { 19 | akkaHttpVersion = '10.1.5' 20 | akkaVersion = '2.5.16' 21 | } 22 | 23 | dependencies { 24 | compile "com.typesafe.akka:akka-http_2.12:$akkaHttpVersion" 25 | compile "com.typesafe.akka:akka-http-jackson_2.12:$akkaHttpVersion" 26 | compile "com.typesafe.akka:akka-stream_2.12:$akkaVersion" 27 | compile group: 'com.typesafe.akka', name: 'akka-http-jackson_2.12', version: akkaHttpVersion 28 | 29 | testCompile "com.typesafe.akka:akka-http-testkit_2.12:$akkaHttpVersion" 30 | testCompile "com.typesafe.akka:akka-stream-testkit_2.12:$akkaVersion" 31 | testCompile 'junit:junit:4.12' 32 | testCompile "org.assertj:assertj-core:3.11.1" 33 | } 34 | -------------------------------------------------------------------------------- /humblecode/src/main/java/com/humblecode/humblecode/model/Course.java: -------------------------------------------------------------------------------- 1 | package com.humblecode.humblecode.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import org.springframework.data.annotation.Id; 6 | import org.springframework.data.mongodb.core.mapping.Document; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.UUID; 11 | 12 | @Data 13 | @AllArgsConstructor 14 | @Document 15 | public class Course { 16 | 17 | @Id UUID id = UUID.randomUUID(); 18 | 19 | public String name; 20 | public long price = 2000; // $20.00 is default price 21 | 22 | public final List segments = new ArrayList<>(); 23 | 24 | public Course(String name) { 25 | this.name = name; 26 | } 27 | 28 | public void setSegments(List segments) { 29 | this.segments.clear(); 30 | this.segments.addAll(segments); 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | return "Course{" + 36 | "name='" + name + '\'' + 37 | ", price=" + price + 38 | '}'; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /akka-http-java/src/test/java/com/github/adamldavis/akkhttp/MessageRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.github.adamldavis.akkhttp; 2 | 3 | import com.github.adamldavis.akkahttp.ChatMessage; 4 | import com.github.adamldavis.akkahttp.MessageRepository; 5 | import org.junit.After; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | import java.util.concurrent.ExecutionException; 10 | 11 | import static org.assertj.core.api.Assertions.assertThat; 12 | 13 | public class MessageRepositoryTest { 14 | 15 | MessageRepository repository; 16 | 17 | @Before 18 | public void setup() { 19 | repository = new MessageRepository(); 20 | } 21 | @After 22 | public void tearDown() { 23 | repository = null; 24 | } 25 | 26 | @Test(timeout = 2000) 27 | public void save_should_take_a_while() throws ExecutionException, InterruptedException { 28 | long start = System.currentTimeMillis(); 29 | 30 | repository.save(new ChatMessage("foo", "testing 123")).toCompletableFuture().get(); 31 | 32 | long took = System.currentTimeMillis() - start; 33 | 34 | assertThat(took).isGreaterThan(498); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /humblecode/src/main/resources/templates/login.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | ${applicationName}: Login 4 | 5 | 6 | 7 | 8 | 9 |
10 |

${applicationName}

11 |
12 |
13 | 14 |

Login with Username and Password

15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
User:
Password:
34 |
35 |
36 | 37 | 38 | -------------------------------------------------------------------------------- /RxAndroidTest/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /RxAndroidTest/.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # IntelliJ 36 | *.iml 37 | .idea/workspace.xml 38 | .idea/tasks.xml 39 | .idea/gradle.xml 40 | .idea/assetWizardSettings.xml 41 | .idea/dictionaries 42 | .idea/libraries 43 | .idea/caches 44 | 45 | # Keystore files 46 | # Uncomment the following line if you do not want to check your keystore files in. 47 | #*.jks 48 | 49 | # External native build folder generated in Android Studio 2.2 and later 50 | .externalNativeBuild 51 | 52 | # Google Services (e.g. APIs or Firebase) 53 | google-services.json 54 | 55 | # Freeline 56 | freeline.py 57 | freeline/ 58 | freeline_project_description.json 59 | 60 | # fastlane 61 | fastlane/report.xml 62 | fastlane/Preview.html 63 | fastlane/screenshots 64 | fastlane/test_output 65 | fastlane/readme.md 66 | -------------------------------------------------------------------------------- /akka-http-java/src/main/resources/akkahttp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello Akka HTTP! 6 | 24 | 25 | 26 | 27 |
28 | Username:
29 | Message:
30 | 31 |
32 |
33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /humblecode/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | springBootVersion = '2.0.3.RELEASE' 4 | } 5 | repositories { 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 10 | } 11 | } 12 | 13 | apply plugin: 'java' 14 | apply plugin: 'eclipse' 15 | apply plugin: 'org.springframework.boot' 16 | apply plugin: 'io.spring.dependency-management' 17 | apply plugin: 'idea' 18 | 19 | 20 | group = 'com.humblecode' 21 | version = '0.0.1-SNAPSHOT' 22 | sourceCompatibility = 1.8 23 | 24 | repositories { 25 | mavenCentral() 26 | } 27 | 28 | dependencies { 29 | compile 'com.stripe:stripe-java:5.38.0' 30 | 31 | compile('org.springframework.boot:spring-boot-starter-data-mongodb-reactive') 32 | compile('org.springframework.boot:spring-boot-starter-freemarker') 33 | compile('org.springframework.boot:spring-boot-starter-security') 34 | compile('org.springframework.boot:spring-boot-starter-webflux') 35 | compile 'io.projectreactor.ipc:reactor-netty' 36 | compileOnly('org.projectlombok:lombok') 37 | 38 | testCompile('org.springframework.boot:spring-boot-starter-test') 39 | testCompile('de.flapdoodle.embed:de.flapdoodle.embed.mongo') 40 | testCompile('io.projectreactor:reactor-test') 41 | testCompile('org.springframework.security:spring-security-test') 42 | } 43 | -------------------------------------------------------------------------------- /humblecode/src/main/resources/static/css/base.css: -------------------------------------------------------------------------------- 1 | /* Sticky footer styles 2 | -------------------------------------------------- */ 3 | 4 | html, 5 | body { 6 | height: 100%; 7 | /* The html and body elements cannot have any padding or margin. */ 8 | } 9 | 10 | /* Wrapper for page content to push down footer */ 11 | #wrap { 12 | min-height: 100%; 13 | height: auto !important; 14 | height: 100%; 15 | /* Negative indent footer by it's height */ 16 | margin: 0 auto -60px; 17 | } 18 | 19 | /* Set the fixed height of the footer here */ 20 | #push, 21 | #footer { 22 | height: 60px; 23 | } 24 | #footer { 25 | background-color: #f5f5f5; 26 | padding: 0; 27 | } 28 | 29 | /* Lastly, apply responsive CSS fixes as necessary */ 30 | @media (max-width: 767px) { 31 | #footer { 32 | margin-left: -20px; 33 | margin-right: -20px; 34 | padding-left: 20px; 35 | padding-right: 20px; 36 | } 37 | } 38 | 39 | /* Custom page CSS 40 | -------------------------------------------------- */ 41 | /* Not required for template or sticky footer method. */ 42 | 43 | #wrap > .container { 44 | padding-top: 60px; 45 | } 46 | .container .credit { 47 | margin: 20px 0; 48 | } 49 | 50 | /*code { 51 | font-size: 80%; 52 | }*/ 53 | 54 | .tags { 55 | display: flex; 56 | } 57 | .tags li { 58 | color: green; 59 | padding: 1em; 60 | list-style: none; 61 | } 62 | .tags li:hover { background: lightgray } 63 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Freeware License, some rights reserved 2 | 3 | Copyright (c) 2019 Adam L. Davis 4 | 5 | Permission is hereby granted, free of charge, to anyone obtaining a copy 6 | of this software and associated documentation files (the "Software"), 7 | to work with the Software within the limits of freeware distribution and fair use. 8 | This includes the rights to use, copy, and modify the Software for personal use. 9 | Users are also allowed and encouraged to submit corrections and modifications 10 | to the Software for the benefit of other users. 11 | 12 | It is not allowed to reuse, modify, or redistribute the Software for 13 | commercial use in any way, or for a user’s educational materials such as books 14 | or blog articles without prior permission from the copyright holder. 15 | 16 | The above copyright notice and this permission notice need to be included 17 | in all copies or substantial portions of the software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS OR APRESS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | 27 | 28 | -------------------------------------------------------------------------------- /akka-http-java/src/main/java/com/github/adamldavis/akkahttp/ChatMessage.java: -------------------------------------------------------------------------------- 1 | package com.github.adamldavis.akkahttp; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | import java.util.Objects; 7 | 8 | public class ChatMessage { 9 | 10 | final String username; 11 | final String message; 12 | 13 | @JsonCreator 14 | public ChatMessage(@JsonProperty("username") String username, 15 | @JsonProperty("message") String message) { 16 | this.username = username; 17 | this.message = message; 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return "ChatMessage {username='" + username + '\'' + 23 | ", message='" + message + '\'' + '}'; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object o) { 28 | if (this == o) return true; 29 | if (!(o instanceof ChatMessage)) return false; 30 | ChatMessage that = (ChatMessage) o; 31 | return Objects.equals(username, that.username) && 32 | Objects.equals(message, that.message); 33 | } 34 | 35 | @Override 36 | public int hashCode() { 37 | return Objects.hash(username, message); 38 | } 39 | 40 | public String getUsername() { 41 | return username; 42 | } 43 | 44 | public String getMessage() { 45 | return message; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /RxAndroidTest/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.adamldavis.rxadroidtest" 7 | minSdkVersion 26 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | compileOptions { 20 | sourceCompatibility '1.8' 21 | targetCompatibility '1.8' 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(dir: 'libs', include: ['*.jar']) 27 | implementation 'com.android.support:appcompat-v7:28.0.0-rc02' 28 | implementation 'com.android.support:design:28.0.0-rc02' 29 | // begin changes 30 | implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' 31 | implementation 'io.reactivex.rxjava2:rxjava:2.2.2' 32 | implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1' 33 | implementation 'com.trello.rxlifecycle2:rxlifecycle:2.2.2' 34 | implementation 'com.trello.rxlifecycle2:rxlifecycle-android:2.2.2' 35 | implementation 'com.trello.rxlifecycle2:rxlifecycle-components:2.2.2' 36 | // end changes 37 | testImplementation 'junit:junit:4.12' 38 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 39 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 40 | } 41 | -------------------------------------------------------------------------------- /reactive-streams-in-java/src/main/java/com/github/adamldavis/MessageController.java: -------------------------------------------------------------------------------- 1 | package com.github.adamldavis; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.Random; 6 | 7 | import io.reactivex.Observable; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.stereotype.Controller; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.PathVariable; 13 | 14 | @Controller 15 | public class MessageController { 16 | 17 | @Autowired 18 | private Channel channel; 19 | 20 | final List types = Arrays.asList("Error", "Warning", "Info"); 21 | 22 | final String INFO = "Channel is running? %s, name: %s, pollCount: %d. (Use /messages/100 to add 100 messages)\n"; 23 | final String POSTED = "Posted %d messages.\n"; 24 | 25 | @GetMapping("/messages/{param}") 26 | public ResponseEntity createMessages(@PathVariable Integer param) { 27 | final Random rnd = new Random(); 28 | // using RxJava to create the messages: 29 | Observable.range(0, param) 30 | .subscribeOn(io.reactivex.schedulers.Schedulers.computation()) 31 | .map(i -> types.get(rnd.nextInt(3)) + ": message " + i) 32 | .doOnNext(msg -> channel.publish(msg)) 33 | .forEach(msg -> System.out.println("Posted message : " + msg)); 34 | 35 | return ResponseEntity.ok(String.format(POSTED, param) + getInfo()); 36 | } 37 | 38 | @GetMapping("/") 39 | public ResponseEntity info() { 40 | return ResponseEntity.ok(getInfo()); 41 | } 42 | 43 | private String getInfo() { 44 | return String.format(INFO, channel.isAlive(), channel.getName(), channel.pollCount.get()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /humblecode/src/main/resources/templates/home.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ${applicationName}: Learn how to code 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | 24 | 25 | 26 | 27 |
28 | 29 | <#if name == "">Sign up today! 30 | 31 | 32 | 33 | 36 | 37 |
38 | 39 | 42 | 43 |
44 | Name: 45 | Price: 46 | 47 |
48 | 49 |
50 | 51 | -------------------------------------------------------------------------------- /RxAndroidTest/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /humblecode/src/main/resources/templates/account.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ${applicationName}: Learn how to code 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | 32 | 35 | 36 |
37 | 38 |
39 |

Upgrade now to get access to all courses.

40 |

Includes: Courses, Tests, Certificates upon completion.

41 |
42 | 43 |
44 | 53 |
54 | 55 |
56 | 57 | -------------------------------------------------------------------------------- /humblecode/src/main/java/com/humblecode/humblecode/model/User.java: -------------------------------------------------------------------------------- 1 | package com.humblecode.humblecode.model; 2 | 3 | import lombok.Data; 4 | import org.springframework.data.annotation.Id; 5 | import org.springframework.data.mongodb.core.mapping.Document; 6 | import org.springframework.security.core.GrantedAuthority; 7 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 8 | import org.springframework.security.core.userdetails.UserDetails; 9 | 10 | import java.util.*; 11 | 12 | @Data 13 | @Document 14 | public class User implements UserDetails { 15 | 16 | static final String DEFAULT = "default"; 17 | static final String MONTHLY = "monthly"; 18 | static final String YEARLY = "yearly"; 19 | 20 | @Id 21 | public UUID id = UUID.randomUUID(); 22 | 23 | public String username; 24 | 25 | public String credentials; 26 | 27 | public String accountType = DEFAULT; //default/monthly/etc. 28 | 29 | public String loginType; // github/facebook/etc. 30 | 31 | public List courseIdsPaidFor = new LinkedList<>(); 32 | 33 | public List testResults = new LinkedList<>(); 34 | 35 | public User() {} 36 | public User(String username, String credentials) { 37 | this.username = username; 38 | this.credentials = credentials; 39 | } 40 | 41 | @Override 42 | public Collection getAuthorities() { 43 | return Arrays.asList(new SimpleGrantedAuthority("user")); 44 | } 45 | 46 | @Override 47 | public String getPassword() { 48 | return credentials; 49 | } 50 | 51 | @Override 52 | public String getUsername() { 53 | return username; 54 | } 55 | 56 | @Override 57 | public boolean isAccountNonExpired() { 58 | return true; 59 | } 60 | 61 | @Override 62 | public boolean isAccountNonLocked() { 63 | return true; 64 | } 65 | 66 | @Override 67 | public boolean isCredentialsNonExpired() { 68 | return true; 69 | } 70 | 71 | @Override 72 | public boolean isEnabled() { 73 | return true; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /humblecode/src/main/java/com/humblecode/humblecode/rest/PaymentControl.java: -------------------------------------------------------------------------------- 1 | package com.humblecode.humblecode.rest; 2 | 3 | import com.stripe.Stripe; 4 | import com.stripe.model.Charge; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.web.bind.annotation.PostMapping; 9 | import org.springframework.web.bind.annotation.RequestBody; 10 | import org.springframework.web.bind.annotation.RestController; 11 | import reactor.core.publisher.Mono; 12 | 13 | import java.util.Map; 14 | 15 | @RestController 16 | public class PaymentControl { 17 | 18 | private static final Logger logger = LoggerFactory.getLogger(PaymentControl.class); 19 | 20 | @Value("${app.stripe.secret}") 21 | String secret; 22 | 23 | @PostMapping(value = "/pay", consumes = "application/x-www-form-urlencoded;charset=UTF-8") 24 | public Mono postCharge(@RequestBody final Map params) { 25 | // Set your secret key: remember to change this to your live secret key in production 26 | // See your keys here: https://dashboard.stripe.com/account/apikeys 27 | Stripe.apiKey = secret; 28 | 29 | // These parameters come from the request body and we should probably validate them. 30 | // params.put("amount", 999); 31 | // params.put("currency", "usd"); 32 | // params.put("source", "tok_visa"); 33 | // params.put("receipt_email", "jenny.rosen@example.com"); 34 | return Mono. create(callback -> { 35 | try { 36 | Charge charge = Charge.create(params); 37 | 38 | logger.info("Charge:" + charge.getDescription() + " at " + 39 | System.currentTimeMillis() + " of amount: $" + charge.getAmount() / 100 + "." 40 | + (charge.getAmount() % 100) + " was " + charge.getStatus()); 41 | 42 | callback.success(charge.getStatus()); 43 | } catch (Throwable ex) { 44 | callback.error(ex); 45 | } 46 | }).doOnError(e -> logger.error(e.getMessage(), e)); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /humblecode/src/main/java/com/humblecode/humblecode/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.humblecode.humblecode; 2 | 3 | import com.humblecode.humblecode.data.UserRepository; 4 | import com.humblecode.humblecode.model.User; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; 8 | import org.springframework.security.config.web.server.ServerHttpSecurity; 9 | import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; 10 | import org.springframework.security.core.userdetails.UserDetails; 11 | import org.springframework.security.crypto.password.PasswordEncoder; 12 | import org.springframework.security.web.server.SecurityWebFilterChain; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | @EnableWebFluxSecurity 18 | public class SecurityConfig { 19 | 20 | @Bean 21 | public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { 22 | http 23 | .authorizeExchange() 24 | .pathMatchers("/api/**", "/css/**", "/js/**", "/images/**", "/").permitAll() 25 | .pathMatchers("/user/**").hasAuthority("user") 26 | .and() 27 | .csrf() 28 | .and() 29 | .formLogin(); 30 | return http.build(); 31 | } 32 | 33 | @Bean 34 | public MapReactiveUserDetailsService userDetailsService(@Autowired UserRepository userRepository) { 35 | List userDetails = new ArrayList<>(); 36 | userDetails.addAll(userRepository.findAll().collectList().block()); 37 | if (userDetails.isEmpty()) { // here for tests to work 38 | userDetails.add(new User("user1", "password")); 39 | } 40 | return new MapReactiveUserDetailsService(userDetails); 41 | } 42 | 43 | @Bean 44 | public PasswordEncoder myPasswordEncoder() { 45 | // never do this in production of course 46 | return new PasswordEncoder() { 47 | @Override 48 | public String encode(CharSequence charSequence) { 49 | return charSequence.toString(); 50 | } 51 | @Override 52 | public boolean matches(CharSequence charSequence, String s) { 53 | return charSequence.equals(s); 54 | } 55 | }; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /RxAndroidTest/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /humblecode/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /humblecode/src/main/resources/static/js/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.2",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b)}(this,document); -------------------------------------------------------------------------------- /reactive-streams-in-java/src/main/java/com/github/adamldavis/Application.java: -------------------------------------------------------------------------------- 1 | package com.github.adamldavis; 2 | 3 | import akka.actor.ActorSystem; 4 | import akka.stream.ActorMaterializer; 5 | import akka.stream.OverflowStrategy; 6 | import akka.stream.javadsl.Sink; 7 | import akka.stream.javadsl.Source; 8 | import org.springframework.boot.SpringApplication; 9 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.ComponentScan; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.context.annotation.Lazy; 14 | 15 | import reactor.core.publisher.Flux; 16 | import reactor.core.scheduler.Schedulers; 17 | 18 | @Configuration 19 | @EnableAutoConfiguration 20 | @ComponentScan 21 | public class Application { 22 | 23 | @Bean 24 | Flux createMessageFlux(final Channel channel) { 25 | // using Reactor to create a Flux linked to the Channel: 26 | Flux bridge = Flux.create(sink -> { 27 | sink.onRequest(n -> channel.poll(n)) // 1 28 | .onCancel(channel::cancel) // 2 29 | .onDispose(channel::close); // 3 30 | 31 | channel.register(sink::next); //4 32 | }); 33 | return bridge; 34 | } 35 | 36 | @Lazy(false) 37 | @Bean 38 | MessageConsumer createMessageConsumer(Flux messageFlux) { 39 | MessageConsumer messageConsumer = new MessageConsumer("Reactor"); 40 | // using Reactor to consume messages: 41 | messageFlux.publishOn(Schedulers.newSingle("message-pub")) 42 | .subscribeOn(Schedulers.elastic()) 43 | .onBackpressureBuffer(100) // 100 max buffer 44 | .subscribe(s -> messageConsumer.accept(s)); 45 | 46 | return messageConsumer; 47 | } 48 | 49 | @Lazy(false) 50 | @Bean 51 | MessageConsumer createAkkaMessageConsumer(Flux messageFlux, ActorSystem actorSystem) { 52 | MessageConsumer messageConsumer = new MessageConsumer("Akka"); 53 | // using Akka Streams to consume messages: 54 | ActorMaterializer mat = ActorMaterializer.create(actorSystem); 55 | 56 | Source.fromPublisher(messageFlux) 57 | .buffer(100, OverflowStrategy.backpressure()) // 100 max buffer 58 | .to(Sink.foreach(msg -> messageConsumer.accept(msg))) 59 | .run(mat); 60 | 61 | return messageConsumer; 62 | } 63 | 64 | @Bean 65 | ActorSystem createActorSystem() { 66 | return ActorSystem.create(); 67 | } 68 | 69 | public static void main(String[] args) { 70 | SpringApplication.run(Application.class, args); 71 | } 72 | } -------------------------------------------------------------------------------- /humblecode/src/main/java/com/humblecode/humblecode/rest/CourseControl.java: -------------------------------------------------------------------------------- 1 | package com.humblecode.humblecode.rest; 2 | 3 | import com.humblecode.humblecode.data.CourseRepository; 4 | import com.humblecode.humblecode.model.Course; 5 | import com.humblecode.humblecode.model.Segment; 6 | import org.springframework.http.MediaType; 7 | import org.springframework.web.bind.annotation.*; 8 | import reactor.core.publisher.Flux; 9 | import reactor.core.publisher.Mono; 10 | 11 | import java.util.Map; 12 | import java.util.UUID; 13 | 14 | @RestController 15 | public class CourseControl { 16 | 17 | final CourseRepository courseRepository; 18 | 19 | public CourseControl(CourseRepository courseRepository) { 20 | this.courseRepository = courseRepository; 21 | } 22 | 23 | @GetMapping("/api/courses") 24 | public Flux getCourses() { 25 | return courseRepository.findAll(); 26 | } 27 | 28 | @GetMapping("/api/course/{id}") 29 | public Mono getCourse(@PathVariable("id") String id) { 30 | return courseRepository.findById(UUID.fromString(id)); 31 | } 32 | 33 | @PostMapping(value = "/api/course", consumes = MediaType.APPLICATION_JSON_VALUE) 34 | public Mono saveCourse(@RequestBody Map body) { 35 | Course course = new Course((String) body.get("name")); 36 | 37 | course.price = Long.parseLong(body.get("price").toString()); 38 | 39 | return courseRepository.insert(course); 40 | } 41 | 42 | @PutMapping(value = "/api/course/{id}", consumes = MediaType.APPLICATION_JSON_VALUE) 43 | public Mono updateCourse(@PathVariable("id") String id, @RequestBody Map body) { 44 | 45 | Mono courseMono = courseRepository.findById(UUID.fromString(id)); 46 | 47 | return courseMono.flatMap(course -> { 48 | if (body.containsKey("price")) course.price = Long.parseLong(body.get("price").toString()); 49 | if (body.containsKey("name")) course.name = (String) body.get("name"); 50 | return courseRepository.save(course); 51 | }); 52 | } 53 | 54 | @PostMapping(value = "/api/course/{id}/segment", consumes = MediaType.APPLICATION_JSON_VALUE) 55 | public Mono saveSegment(@PathVariable("id") String id, Segment segment) { 56 | Mono courseMono = courseRepository.findById(UUID.fromString(id)); 57 | 58 | return courseMono.flatMap(course -> { 59 | int index = course.segments.indexOf(segment); 60 | 61 | course.segments.remove(segment); // remove it if already there 62 | if (index >= 0) { 63 | course.segments.add(index, segment); 64 | } else { 65 | course.segments.add(segment); 66 | } 67 | return courseRepository.save(course); 68 | }); 69 | } 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /reactive-streams-in-java/src/main/java/com/github/adamldavis/Channel.java: -------------------------------------------------------------------------------- 1 | package com.github.adamldavis; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Deque; 5 | import java.util.List; 6 | import java.util.concurrent.ConcurrentLinkedDeque; 7 | import java.util.concurrent.atomic.AtomicLong; 8 | 9 | import org.springframework.stereotype.Service; 10 | 11 | import io.reactivex.functions.Consumer; 12 | 13 | /** 14 | * Channel uses a Deque to act as a buffer of messages. 15 | * Merely here as a demo of integrating an outside system with Reactive Streams. 16 | * In practice this could probably be better replaced by PublishProcessor in RxJava or a Broadcast in Akka Streams 17 | * or UnicastProcessor in Reactor. 18 | * 19 | * This is definitely not the best implementation available for this concept. 20 | * For example, this Channel does not make any guarantee about ordering. Again, this is just for show. 21 | */ 22 | @Service 23 | public class Channel extends Thread { 24 | 25 | final Deque deque = new ConcurrentLinkedDeque<>(); 26 | 27 | final List> listeners = new ArrayList<>(); 28 | 29 | AtomicLong pollCount = new AtomicLong(0); 30 | 31 | /** Non-Blocking and increments the pollCount by n. */ 32 | public void poll(long n) { 33 | synchronized (deque) { 34 | pollCount.getAndAdd(n); 35 | System.out.println("--> poll (" + n + ") called"); 36 | if (!isAlive()) start(); 37 | } 38 | } 39 | 40 | /** Loops forever, gets up to pollCount messages and sends them to the Listeners. */ 41 | @Override 42 | public void run() { 43 | while (true) { 44 | loop(); 45 | } 46 | } 47 | 48 | private void loop() { 49 | int count = 0; 50 | 51 | for (String msg; count < pollCount.get() && !deque.isEmpty(); count++) { 52 | msg = deque.pop(); 53 | final String message = msg; 54 | listeners.forEach(listener -> { 55 | try { 56 | listener.accept(message); 57 | } catch (Exception e) { 58 | e.printStackTrace(); 59 | } 60 | }); 61 | } 62 | pollCount.getAndAdd(0 - count); // decrement by number of messages accepted. 63 | try { 64 | Thread.sleep(25); 65 | } // sleep to allow other threads to go 66 | catch (Exception ignore) {} 67 | } 68 | 69 | public void cancel() { 70 | deque.clear(); 71 | super.interrupt(); 72 | } 73 | 74 | public void close() { 75 | deque.clear(); 76 | super.interrupt(); 77 | } 78 | 79 | public void publish(String message) { 80 | deque.add(message); 81 | } 82 | 83 | public void register(Consumer listener) { 84 | listeners.add(listener); 85 | } 86 | } -------------------------------------------------------------------------------- /humblecode/src/main/java/com/humblecode/humblecode/web/WebControl.java: -------------------------------------------------------------------------------- 1 | package com.humblecode.humblecode.web; 2 | 3 | import com.humblecode.humblecode.data.CourseRepository; 4 | import com.humblecode.humblecode.data.UserRepository; 5 | import com.humblecode.humblecode.model.Course; 6 | import com.humblecode.humblecode.model.User; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.stereotype.Controller; 10 | import org.springframework.ui.Model; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RequestMethod; 14 | import org.springframework.web.bind.annotation.RequestParam; 15 | import reactor.core.publisher.Flux; 16 | import reactor.core.publisher.Mono; 17 | import reactor.core.scheduler.Schedulers; 18 | 19 | import javax.annotation.PostConstruct; 20 | import java.security.Principal; 21 | 22 | @Controller 23 | public class WebControl { 24 | 25 | @Value("${app.name}") 26 | String appName; 27 | 28 | @Autowired 29 | CourseRepository courseRepository; 30 | @Autowired 31 | UserRepository userRepository; 32 | 33 | @PostConstruct 34 | public void setup() { 35 | courseRepository.count().blockOptional().filter(count -> count == 0).ifPresent(it -> 36 | Flux.just( 37 | new Course("Beginning Java"), 38 | new Course("Advanced Java"), 39 | new Course("Reactive Streams in Java")) 40 | .doOnNext(c -> System.out.println(c.toString())) 41 | .flatMap(courseRepository::save).subscribeOn(Schedulers.single()) 42 | .subscribe() // need to actually execute save*/ 43 | ); 44 | // just adding dummy user for demo purposes: 45 | userRepository.count().blockOptional().filter(count -> count == 0).ifPresent(it -> 46 | Flux.just(new User("user", "password")) 47 | .flatMap(userRepository::save).subscribeOn(Schedulers.single()) 48 | .subscribe() 49 | ); 50 | } 51 | 52 | @GetMapping("/") 53 | public Mono home(Model model, Principal principal) { 54 | model.addAttribute("name", principal == null ? "" : principal.getName()); 55 | model.addAttribute("applicationName", appName); 56 | return Mono.just("home"); 57 | } 58 | 59 | @GetMapping("/login-error") 60 | public String loginError(Model model) { 61 | model.addAttribute("applicationName", appName); 62 | model.addAttribute("error", "Login failed."); 63 | return "login"; 64 | } 65 | 66 | @GetMapping("/user/account") 67 | public String userAccount(Model model, Principal principal) { 68 | model.addAttribute("applicationName", appName); 69 | model.addAttribute("username", principal.getName()); 70 | return "account"; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /humblecode/src/test/java/com/humblecode/humblecode/HumblecodeApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.humblecode.humblecode; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 8 | import org.springframework.boot.test.web.client.TestRestTemplate; 9 | import org.springframework.http.*; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | 12 | import java.util.Arrays; 13 | 14 | import static org.assertj.core.api.Assertions.assertThat; 15 | 16 | @RunWith(SpringRunner.class) 17 | @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) 18 | public class HumblecodeApplicationTests { 19 | 20 | @Autowired 21 | private TestRestTemplate testRestTemplate; 22 | 23 | @Test 24 | public void testFreeMarkerTemplate() { 25 | ResponseEntity entity = this.testRestTemplate.getForEntity("/", 26 | String.class); 27 | assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); 28 | assertThat(entity.getBody()).contains("Welcome to"); 29 | } 30 | 31 | @Test 32 | public void testFreeMarkerErrorTemplate() { 33 | HttpHeaders headers = new HttpHeaders(); 34 | headers.setAccept(Arrays.asList(MediaType.TEXT_HTML)); 35 | HttpEntity requestEntity = new HttpEntity<>(headers); 36 | 37 | ResponseEntity responseEntity = this.testRestTemplate 38 | .exchange("/css/foobar", HttpMethod.GET, requestEntity, String.class); 39 | 40 | assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); 41 | assertThat(responseEntity.getBody()).contains("404 Not Found"); 42 | } 43 | 44 | @Test 45 | public void testCSSCanBeGotten() { 46 | HttpHeaders headers = new HttpHeaders(); 47 | headers.setAccept(Arrays.asList(MediaType.TEXT_HTML)); 48 | HttpEntity requestEntity = new HttpEntity<>(headers); 49 | 50 | ResponseEntity responseEntity = this.testRestTemplate 51 | .exchange("/css/base.css", HttpMethod.GET, requestEntity, String.class); 52 | 53 | assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK); 54 | } 55 | 56 | @Test 57 | public void testLoginPageCanBeGotten() { 58 | HttpHeaders headers = new HttpHeaders(); 59 | headers.setAccept(Arrays.asList(MediaType.TEXT_HTML)); 60 | HttpEntity requestEntity = new HttpEntity<>(headers); 61 | 62 | ResponseEntity responseEntity = this.testRestTemplate 63 | .exchange("/user/account", HttpMethod.GET, requestEntity, String.class); 64 | 65 | assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK); 66 | } 67 | 68 | @Test 69 | public void testGetCourses() { 70 | HttpHeaders headers = new HttpHeaders(); 71 | headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); 72 | HttpEntity requestEntity = new HttpEntity<>(headers); 73 | 74 | ResponseEntity response = this.testRestTemplate 75 | .exchange("/api/courses", HttpMethod.GET, requestEntity, String.class); 76 | 77 | assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); 78 | assertThat(response.getBody()).contains("\"name\":\"Beginning Java\",\"price\":2000"); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /humblecode/src/main/resources/static/js/main.js: -------------------------------------------------------------------------------- 1 | 2 | var errorHandler = (err) => { alert("Sorry, there was a problem. " + err); console.log(err) } 3 | 4 | var HC = { 5 | loadCourses: function() { 6 | jQuery.ajax({method: 'get', url: '/api/courses'}).done( 7 | function(data) { 8 | var list = data; 9 | var ul = jQuery('
    '); 10 | list.forEach((crs) => { 11 | ul.append('') 13 | }); 14 | jQuery('#content').html(ul); 15 | } 16 | ).fail( errorHandler ); 17 | }, 18 | toDollars: function(price) { 19 | return '$' + (price/ 100); 20 | }, 21 | course: null, 22 | currentSegment: null, 23 | nextSegment: null, 24 | loadCourse: function(id) { 25 | jQuery.ajax({method: 'get', url: '/api/course/'+id}).done( 26 | function(course) { 27 | console.log("data=" + course); 28 | HC.course = course; 29 | var segments = course.segments; 30 | var ul = jQuery('
      '); 31 | segments.forEach((segment) => { 32 | ul.append('') 34 | }); 35 | jQuery('h1').text(course.name); 36 | jQuery('#content').html(ul); 37 | } 38 | ).fail( errorHandler ); 39 | }, 40 | loadSegment: function(id) { 41 | if (!HC.course) { 42 | console.log("ERROR: course not available"); 43 | return; 44 | } else { 45 | var ids = HC.course.segments.map(it => it.id); 46 | var index = ids.indexOf(id); 47 | HC.currentSegment = HC.course.segments[index]; 48 | if (HC.course.segments.length > index) HC.nextSegment = HC.course.segments[index + 1]; 49 | else HC.nextSegment = null; 50 | //TODO: improve styling of text 51 | jQuery('h1').text(HC.currentSegment.name); 52 | jQuery('#content').text(HC.currentSegment.body); 53 | } 54 | }, 55 | postCourse: function() { 56 | var name = jQuery('#name').val(); 57 | var price = jQuery('#price').val(); 58 | jQuery.ajax({method: 'post', url: '/api/course/', data: {name: name, price: price}}).done( 59 | function(course) { 60 | console.log("data=" + course); 61 | HC.course = course; 62 | var segments = course.segments; 63 | var ul = jQuery('
        '); 64 | segments.forEach((segment) => { 65 | ul.append('') 67 | }); 68 | jQuery('h1').text(course.name); 69 | jQuery('#content').html(ul); 70 | } 71 | ).fail( errorHandler ); 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /RxAndroidTest/app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | 22 | 23 | 27 | 28 | 33 | 34 | 37 | 38 | 46 | 47 | 48 | 49 | 52 | 53 | 64 | 65 | 66 | 67 |