├── .gitignore ├── README.md ├── part1 ├── chapter01 │ ├── README.md │ ├── build.gradle.kts │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ └── Main.kt ├── chapter02 │ ├── README.md │ ├── build.gradle.kts │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ └── Main.kt └── chapter03 │ ├── README.md │ ├── build.gradle.kts │ ├── gradle.properties │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ └── src │ └── main │ ├── java │ ├── AnimalJava.java │ ├── CalcJava.java │ ├── GreeterJava.java │ ├── Hello.java │ ├── HelloByJava.java │ ├── JavaMain.java │ ├── JavaMain2.java │ ├── User.java │ └── UserJava.java │ └── kotlin │ ├── CompanyConstants.kt │ ├── DogKotlin.kt │ ├── GreeterImplKotlin.kt │ ├── HelloByKotlin.kt │ ├── Main.kt │ └── Time.kt ├── part2 ├── book-manager │ ├── .gitignore │ ├── README.md │ ├── build.gradle.kts │ ├── docker │ │ └── docker-compose.yml │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ ├── sql │ │ └── book_manager.sql │ └── src │ │ ├── main │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── book │ │ │ │ └── manager │ │ │ │ ├── BookManagerApplication.kt │ │ │ │ ├── application │ │ │ │ └── service │ │ │ │ │ ├── AdminBookService.kt │ │ │ │ │ ├── AuthenticationService.kt │ │ │ │ │ ├── BookService.kt │ │ │ │ │ ├── RentalService.kt │ │ │ │ │ └── security │ │ │ │ │ └── BookManagerUserDetailsService.kt │ │ │ │ ├── domain │ │ │ │ ├── enum │ │ │ │ │ └── RoleType.kt │ │ │ │ ├── model │ │ │ │ │ ├── Book.kt │ │ │ │ │ ├── BookWithRental.kt │ │ │ │ │ ├── Rental.kt │ │ │ │ │ └── User.kt │ │ │ │ └── repository │ │ │ │ │ ├── BookRepository.kt │ │ │ │ │ ├── RentalRepository.kt │ │ │ │ │ └── UserRepository.kt │ │ │ │ ├── infrastructure │ │ │ │ └── database │ │ │ │ │ ├── mapper │ │ │ │ │ ├── BookDynamicSqlSupport.kt │ │ │ │ │ ├── BookMapper.kt │ │ │ │ │ ├── BookMapperExtensions.kt │ │ │ │ │ ├── RentalDynamicSqlSupport.kt │ │ │ │ │ ├── RentalMapper.kt │ │ │ │ │ ├── RentalMapperExtensions.kt │ │ │ │ │ ├── UserDynamicSqlSupport.kt │ │ │ │ │ ├── UserMapper.kt │ │ │ │ │ ├── UserMapperExtensions.kt │ │ │ │ │ └── custom │ │ │ │ │ │ ├── BookWithRentalMapper.kt │ │ │ │ │ │ └── BookWithRentalMapperExtentions.kt │ │ │ │ │ ├── record │ │ │ │ │ ├── BookRecord.kt │ │ │ │ │ ├── RentalRecord.kt │ │ │ │ │ ├── UserRecord.kt │ │ │ │ │ └── custom │ │ │ │ │ │ └── BookWithRentalRecord.kt │ │ │ │ │ └── repository │ │ │ │ │ ├── BookRepositoryImpl.kt │ │ │ │ │ ├── RentalRepositoryImpl.kt │ │ │ │ │ └── UserRepositoryImpl.kt │ │ │ │ └── presentation │ │ │ │ ├── aop │ │ │ │ └── LoggingAdvice.kt │ │ │ │ ├── config │ │ │ │ ├── HttpSessionConfig.kt │ │ │ │ └── SecurityConfig.kt │ │ │ │ ├── controller │ │ │ │ ├── AdminBookController.kt │ │ │ │ ├── BookController.kt │ │ │ │ └── RentalController.kt │ │ │ │ ├── form │ │ │ │ ├── BookForm.kt │ │ │ │ └── RentalForm.kt │ │ │ │ └── handler │ │ │ │ ├── BookManagerAccessDeniedHandler.kt │ │ │ │ ├── BookManagerAuthenticationEntryPoint.kt │ │ │ │ ├── BookManagerAuthenticationFailureHandler.kt │ │ │ │ └── BookManagerAuthenticationSuccessHandler.kt │ │ └── resources │ │ │ ├── application.yml │ │ │ └── generatorConfig.xml │ │ └── test │ │ ├── kotlin │ │ └── com │ │ │ └── book │ │ │ └── manager │ │ │ ├── BookManagerApplicationTests.kt │ │ │ ├── application │ │ │ └── service │ │ │ │ ├── BookServiceTest.kt │ │ │ │ └── RentalServiceTest.kt │ │ │ ├── domain │ │ │ └── model │ │ │ │ └── BookWithRentalTest.kt │ │ │ └── presentation │ │ │ └── controller │ │ │ └── BookControllerTest.kt │ │ └── resources │ │ └── mockito-extensions │ │ └── org.mockito.plugins.MockMaker ├── chapter04 │ ├── README.md │ └── demo │ │ ├── .gitignore │ │ ├── build.gradle.kts │ │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── settings.gradle.kts │ │ └── src │ │ ├── main │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── demo │ │ │ │ ├── DemoApplication.kt │ │ │ │ ├── Greeter.kt │ │ │ │ ├── GreeterController.kt │ │ │ │ ├── GreeterImpl.kt │ │ │ │ ├── HelloController.kt │ │ │ │ ├── HelloRequest.kt │ │ │ │ └── HelloResponse.kt │ │ └── resources │ │ │ ├── application.properties │ │ │ └── templates │ │ │ └── index.html │ │ └── test │ │ └── kotlin │ │ └── com │ │ └── example │ │ └── demo │ │ └── DemoApplicationTests.kt ├── chapter05 │ ├── README.md │ ├── mybatis-only │ │ ├── build.gradle.kts │ │ ├── gradle.properties │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── settings.gradle │ │ └── src │ │ │ └── main │ │ │ ├── kotlin │ │ │ ├── Main.kt │ │ │ └── database │ │ │ │ ├── UserDynamicSqlSupport.kt │ │ │ │ ├── UserMapper.kt │ │ │ │ ├── UserMapperExtensions.kt │ │ │ │ └── UserRecord.kt │ │ │ └── resources │ │ │ ├── generatorConfig.xml │ │ │ ├── mybatis-config.xml │ │ │ └── package.json │ └── mybatis-with-springboot │ │ └── demo │ │ ├── .gitignore │ │ ├── build.gradle.kts │ │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── settings.gradle.kts │ │ └── src │ │ ├── main │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── demo │ │ │ │ ├── DemoApplication.kt │ │ │ │ ├── Greeter.kt │ │ │ │ ├── GreeterController.kt │ │ │ │ ├── GreeterImpl.kt │ │ │ │ ├── HelloController.kt │ │ │ │ ├── HelloRequest.kt │ │ │ │ ├── HelloResponse.kt │ │ │ │ ├── UserController.kt │ │ │ │ └── database │ │ │ │ ├── UserDynamicSqlSupport.kt │ │ │ │ ├── UserMapper.kt │ │ │ │ ├── UserMapperExtensions.kt │ │ │ │ └── UserRecord.kt │ │ └── resources │ │ │ ├── application.yml │ │ │ ├── generatorConfig.xml │ │ │ └── templates │ │ │ └── index.html │ │ └── test │ │ └── kotlin │ │ └── com │ │ └── example │ │ └── demo │ │ └── DemoApplicationTests.kt └── front │ └── book-manager │ ├── .babelrc │ ├── .editorconfig │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .gitignore │ ├── .postcssrc.js │ ├── README.md │ ├── build │ ├── build.js │ ├── check-versions.js │ ├── logo.png │ ├── utils.js │ ├── vue-loader.conf.js │ ├── webpack.base.conf.js │ ├── webpack.dev.conf.js │ └── webpack.prod.conf.js │ ├── config │ ├── dev.env.js │ ├── index.js │ ├── prod.env.js │ └── test.env.js │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── App.vue │ ├── api │ │ ├── auth.js │ │ ├── book.js │ │ ├── client.js │ │ └── rental.js │ ├── assets │ │ ├── css │ │ │ └── base.css │ │ └── logo.png │ ├── components │ │ ├── BookDeleteCompleted.vue │ │ ├── BookDetail.vue │ │ ├── BookList.vue │ │ ├── BookRegister.vue │ │ ├── BookRegisterCompleted.vue │ │ ├── BookUpdate.vue │ │ ├── BookUpdateCompleted.vue │ │ ├── HelloWorld.vue │ │ └── Login.vue │ ├── main.js │ └── router │ │ └── index.js │ └── static │ └── .gitkeep └── part3 ├── chapter09 ├── README.md ├── grpc-only │ ├── build.gradle.kts │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ └── src │ │ └── main │ │ ├── kotlin │ │ └── example │ │ │ └── greeter │ │ │ ├── client │ │ │ └── GreeterClient.kt │ │ │ └── server │ │ │ ├── GreeterServer.kt │ │ │ └── GreeterService.kt │ │ └── proto │ │ └── greeter.proto └── grpc-with-springboot │ ├── .gitignore │ ├── build.gradle.kts │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ └── src │ ├── main │ ├── kotlin │ │ └── com │ │ │ └── example │ │ │ └── grpc │ │ │ └── springboot │ │ │ ├── Application.kt │ │ │ ├── client │ │ │ └── GreeterClientController.kt │ │ │ └── server │ │ │ └── GreeterService.kt │ ├── proto │ │ └── greeter.proto │ └── resources │ │ └── application.properties │ └── test │ └── kotlin │ └── com │ └── example │ └── grpc │ └── springboot │ └── ApplicationTests.kt ├── chapter10 ├── .gitignore ├── README.md ├── build.gradle.kts ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── resources │ ├── application.conf │ └── logback.xml ├── settings.gradle.kts └── src │ ├── Application.kt │ └── example │ └── route │ ├── BookRoute.kt │ ├── GreetingRoute.kt │ └── UserRoute.kt ├── chapter11 ├── README.md ├── build.gradle.kts ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src │ └── main │ └── kotlin │ ├── CrudExample.kt │ ├── DaoExample.kt │ ├── DslExample.kt │ └── Member.kt └── chapter12 ├── README.md ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src ├── main └── kotlin │ └── example │ └── kotest │ ├── Number.kt │ ├── UserRepository.kt │ └── UserService.kt └── test └── kotlin └── example └── kotest ├── NumberTestByAnnotationSpec.kt ├── NumberTestByBehaviorSpec.kt ├── NumberTestByStringSpec.kt └── UserServiceTest.kt /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !part2/front/book-manager/build 5 | !gradle/wrapper/gradle-wrapper.jar 6 | !**/src/main/** 7 | !**/src/test/** 8 | 9 | ### STS ### 10 | .apt_generated 11 | .classpath 12 | .factorypath 13 | .project 14 | .settings 15 | .springBeans 16 | .sts4-cache 17 | 18 | ### IntelliJ IDEA ### 19 | .idea 20 | *.iws 21 | *.iml 22 | *.ipr 23 | out/ 24 | 25 | ### NetBeans ### 26 | /nbproject/private/ 27 | /nbbuild/ 28 | /dist/ 29 | /nbdist/ 30 | /.nb-gradle/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 概要 2 | 書籍「[Kotlin サーバーサイドプログラミング実践開発](https://gihyo.jp/book/2021/978-4-297-11859-4)」のサンプルコードです。 3 | 4 | # ディレクトリ構成 5 | 以下のように各サンプルコードが配置されています。 6 | 7 | - part1 - 第1部 8 | - chapter01 - 第1章 9 | - chapter02 - 第2章 10 | - chapter03 - 第3章 11 | - part2 - 第2部 12 | - chapter04 - 第4章 13 | - chapter05 - 第5章 14 | - book-manager - 6〜8章 15 | - front - 6〜7章で使用するフロントエンドのコード(本誌未掲載) 16 | - part3 - 第3部 17 | - chapter09 - 第9章 18 | - chapter10 - 第10章 19 | - chapter11 - 第11章 20 | - chapter12 - 第12章 21 | 22 | 各部ごとにpart1〜part3ディレクトリに分かれ、その下でさらに各章ごとにchapterXXという名前のディレクトリに分かれています。 23 | 第6〜8章に関しては、通してbook-managerという一つのアプリケーションを作っています。 24 | 詳しい実行方法は、各章のディレクトリ配下のREADME.mdを参照してください。 25 | 26 | # お問合せ先 27 | こちらのリポジトリの内容も含め、書籍に関するお問い合わせは以下のフォームからお願い致します。 28 | 29 | https://gihyo.jp/site/inquiry/book?ISBN=978-4-297-11859-4 30 | -------------------------------------------------------------------------------- /part1/chapter01/README.md: -------------------------------------------------------------------------------- 1 | # 概要 2 | chapter01ディレクトリが、一つのGradleプロジェクトとなっています。 3 | IntelliJ IDEAで [File]->[Open] を選択し、このディレクトリのbuild.gradle.ktsを開いてください。 4 | ダイアログが表示されるので、[Open as Project] を選択してください。 5 | 6 | src/main/kotlin配下のMain.ktに全てのサンプルコードが入っています。 7 | 各コード上の書籍との対応は、以下のようにリスト番号をコメントで記載しています。 8 | 9 | ``` 10 | /** 11 | * リスト1.3.1 12 | */ 13 | ``` 14 | 15 | # 実行方法 16 | クラスの定義などを除く、実行結果の伴うサンプルコードは、リストごとに関数として処理を記述しています。 17 | 一番上にあるmain関数を以下のように書き換え、実行したいリストの関数を呼び出す形に変更することで、動作確認をできます。 18 | 19 | ``` 20 | fun main() { 21 | list1_3_8() 22 | } 23 | ``` 24 | 25 | # 補足 26 | 複数のリストで同じクラス名や関数名を使っている場合など、並べて書かれているとコンパイルエラーになってしまうものは、コメントアウトされています。 27 | 必要に応じてコメントを外し、実行してください。 28 | コンパイルエラーになる例を紹介しているコードに関しても、同様にコメントアウトされています。 29 | -------------------------------------------------------------------------------- /part1/chapter01/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | plugins { 4 | kotlin("jvm") version "1.4.21" 5 | } 6 | 7 | group = "me.take7010" 8 | version = "1.0-SNAPSHOT" 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | tasks.withType() { 15 | kotlinOptions.jvmTarget = "11" 16 | } -------------------------------------------------------------------------------- /part1/chapter01/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | -------------------------------------------------------------------------------- /part1/chapter01/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-takehata/kotlin-server-side-programming-practice/3526abaf27718941f5d36bd2588ec126e9386ae5/part1/chapter01/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part1/chapter01/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part1/chapter01/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /part1/chapter01/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | 2 | rootProject.name = "chapter01" 3 | 4 | -------------------------------------------------------------------------------- /part1/chapter02/README.md: -------------------------------------------------------------------------------- 1 | # 概要 2 | chapter02ディレクトリが、一つのGradleプロジェクトとなっています。 3 | IntelliJ IDEAで [File]->[Open] を選択し、このディレクトリのbuild.gradle.ktsを開いてください。 4 | ダイアログが表示されるので、[Open as Project] を選択してください。 5 | 6 | src/main/kotlin配下のMain.ktに全てのサンプルコードが入っています。 7 | 各コード上の書籍との対応は、以下のようにリスト番号をコメントで記載しています。 8 | 9 | ``` 10 | /** 11 | * リスト2.1.3 12 | */ 13 | ``` 14 | 15 | # 実行方法 16 | クラスの定義などを除く、実行結果の伴うサンプルコードは、リストごとに関数として処理を記述しています。 17 | 一番上にあるmain関数を以下のように書き換え、実行したいリストの関数を呼び出す形に変更することで、動作確認をできます。 18 | 19 | ``` 20 | fun main() { 21 | list2_1_8() 22 | } 23 | ``` 24 | 25 | # 補足 26 | 複数のリストで同じクラス名や関数名を使っている場合など、並べて書かれているとコンパイルエラーになってしまうものは、コメントアウトされています。 27 | 必要に応じてコメントを外し、実行してください。 28 | コンパイルエラーになる例を紹介しているコードに関しても、同様にコメントアウトされています。 29 | -------------------------------------------------------------------------------- /part1/chapter02/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | plugins { 4 | kotlin("jvm") version "1.4.21" 5 | } 6 | 7 | group = "me.take7010" 8 | version = "1.0-SNAPSHOT" 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2") 16 | } 17 | 18 | tasks.withType() { 19 | kotlinOptions.jvmTarget = "11" 20 | } -------------------------------------------------------------------------------- /part1/chapter02/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | -------------------------------------------------------------------------------- /part1/chapter02/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-takehata/kotlin-server-side-programming-practice/3526abaf27718941f5d36bd2588ec126e9386ae5/part1/chapter02/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part1/chapter02/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part1/chapter02/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /part1/chapter02/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | 2 | rootProject.name = "chapter02" 3 | 4 | -------------------------------------------------------------------------------- /part1/chapter03/README.md: -------------------------------------------------------------------------------- 1 | # 概要 2 | chapter03ディレクトリが、一つのGradleプロジェクトとなっています。 3 | IntelliJ IDEAで [File]->[Open] を選択し、このディレクトリのbuild.gradle.ktsを開いてください。 4 | ダイアログが表示されるので、[Open as Project] を選択してください。 5 | 6 | 3章ではJavaのコードも扱っており、以下のようにファイルが配置されています。 7 | 8 | - src/main/kotlin・・・Kotlinのコード 9 | - src/main/java・・・Javaのコード 10 | 11 | 各コード上の書籍との対応は、以下のようにリスト番号をコメントで記載しています。 12 | 13 | ``` 14 | /** 15 | * リスト3.1.2 16 | */ 17 | ``` 18 | 19 | クラスごとにファイルが定義されています。 20 | また、実行処理に関してはKotlinはsrc/main/kotlin配下のMain.kt、Javaはsrc/main/java配下のJavaMain.java、JavaMain2.javaで定義されています。 21 | 22 | # 実行方法 23 | ## Kotlin 24 | クラスの定義などを除く、実行結果の伴うサンプルコードは、リストごとに関数として処理を記述しています。 25 | Main.ktの一番上にあるmain関数を以下のように書き換え、実行したいリストの関数を呼び出す形に変更することで、動作確認をできます。 26 | 27 | ``` 28 | fun main() { 29 | list1_3_8() 30 | } 31 | ``` 32 | 33 | ## Java 34 | JavaMain.java、JavaMain2.javaにそれぞれあるmainメソッドを実行してください。 35 | 36 | # 補足 37 | 複数のリストで同じクラス名や関数名を使っている場合など、並べて書かれているとコンパイルエラーになってしまうものは、コメントアウトされています。 38 | 必要に応じてコメントを外し、実行してください。 39 | コンパイルエラーになる例を紹介しているコードに関しても、同様にコメントアウトされています。 40 | -------------------------------------------------------------------------------- /part1/chapter03/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | plugins { 4 | kotlin("jvm") version "1.4.30" 5 | } 6 | 7 | group = "me.take7010" 8 | version = "1.0-SNAPSHOT" 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | 15 | 16 | tasks.withType() { 17 | kotlinOptions.jvmTarget = "11" 18 | } -------------------------------------------------------------------------------- /part1/chapter03/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | -------------------------------------------------------------------------------- /part1/chapter03/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-takehata/kotlin-server-side-programming-practice/3526abaf27718941f5d36bd2588ec126e9386ae5/part1/chapter03/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part1/chapter03/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part1/chapter03/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /part1/chapter03/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | 2 | rootProject.name = "chapter03" 3 | 4 | -------------------------------------------------------------------------------- /part1/chapter03/src/main/java/AnimalJava.java: -------------------------------------------------------------------------------- 1 | /** 2 | * リスト3.3.1 3 | */ 4 | public class AnimalJava { 5 | public void cry() { 6 | System.out.println("pien"); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /part1/chapter03/src/main/java/CalcJava.java: -------------------------------------------------------------------------------- 1 | @FunctionalInterface 2 | public interface CalcJava { 3 | Integer calc(Integer num1, Integer num2); 4 | } -------------------------------------------------------------------------------- /part1/chapter03/src/main/java/GreeterJava.java: -------------------------------------------------------------------------------- 1 | /** 2 | * リスト3.3.4 3 | */ 4 | public interface GreeterJava { 5 | void hello(); 6 | } -------------------------------------------------------------------------------- /part1/chapter03/src/main/java/Hello.java: -------------------------------------------------------------------------------- 1 | /** 2 | * リスト3.5.1 3 | */ 4 | public class Hello { 5 | public static void main(String[] args) { 6 | System.out.println("Hello World."); 7 | } 8 | } -------------------------------------------------------------------------------- /part1/chapter03/src/main/java/HelloByJava.java: -------------------------------------------------------------------------------- 1 | /** 2 | * リスト3.1.1 3 | */ 4 | public class HelloByJava { 5 | public void printHello() { 6 | System.out.println("Hello Java."); 7 | } 8 | } -------------------------------------------------------------------------------- /part1/chapter03/src/main/java/JavaMain.java: -------------------------------------------------------------------------------- 1 | /** 2 | * リスト3.1.4 3 | */ 4 | public class JavaMain { 5 | public static void main(String[] args) { 6 | HelloByKotlin helloByKotlin = new HelloByKotlin(); 7 | helloByKotlin.printHello(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /part1/chapter03/src/main/java/JavaMain2.java: -------------------------------------------------------------------------------- 1 | public class JavaMain2 { 2 | /** 3 | * リスト3.4.7 4 | */ 5 | // public static void main(String[] args) { 6 | // System.out.println(CompanyConstants.Companion.getMaxEmployeeCount()); 7 | // } 8 | 9 | /** 10 | * リスト3.4.9 11 | */ 12 | public static void main(String[] args) { 13 | System.out.println(CompanyConstants.getMaxEmployeeCount()); 14 | } 15 | } -------------------------------------------------------------------------------- /part1/chapter03/src/main/java/User.java: -------------------------------------------------------------------------------- 1 | /** 2 | * リスト3.5.3 3 | */ 4 | public class User { 5 | private Integer id; 6 | private String name; 7 | 8 | public Integer getId() { 9 | return id; 10 | } 11 | 12 | public void setId(Integer id) { 13 | this.id = id; 14 | } 15 | 16 | public String getName() { 17 | return name; 18 | } 19 | 20 | public void setName(String name) { 21 | this.name = name; 22 | } 23 | } -------------------------------------------------------------------------------- /part1/chapter03/src/main/java/UserJava.java: -------------------------------------------------------------------------------- 1 | public class UserJava { 2 | private Integer id; 3 | private String name; 4 | 5 | public Integer getId() { 6 | return id; 7 | } 8 | 9 | public void setId(Integer id) { 10 | this.id = id; 11 | } 12 | 13 | public String getName() { 14 | return name; 15 | } 16 | 17 | public void setName(String name) { 18 | this.name = name; 19 | } 20 | } -------------------------------------------------------------------------------- /part1/chapter03/src/main/kotlin/CompanyConstants.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * リスト3.4.6 3 | */ 4 | //class CompanyConstants { 5 | // companion object { 6 | // val maxEmployeeCount = 100 7 | // } 8 | //} 9 | 10 | /** 11 | * リスト3.4.8 12 | */ 13 | class CompanyConstants { 14 | companion object { 15 | @JvmStatic 16 | val maxEmployeeCount = 100 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /part1/chapter03/src/main/kotlin/DogKotlin.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * リスト3.3.2 3 | */ 4 | class DogKotlin: AnimalJava() { 5 | override fun cry() { 6 | println("bowwow") 7 | } 8 | } -------------------------------------------------------------------------------- /part1/chapter03/src/main/kotlin/GreeterImplKotlin.kt: -------------------------------------------------------------------------------- 1 | class GreeterImplKotlin: GreeterJava { 2 | override fun hello() { 3 | println("Hello.") 4 | } 5 | } -------------------------------------------------------------------------------- /part1/chapter03/src/main/kotlin/HelloByKotlin.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * リスト3.1.3 3 | */ 4 | class HelloByKotlin { 5 | fun printHello() { 6 | println("Hello Kotlin.") 7 | } 8 | } -------------------------------------------------------------------------------- /part1/chapter03/src/main/kotlin/Main.kt: -------------------------------------------------------------------------------- 1 | import java.util.UUID 2 | 3 | fun main() { 4 | list3_1_2() 5 | } 6 | 7 | /** 8 | * リスト3.1.2 9 | */ 10 | fun list3_1_2() { 11 | val hello = HelloByJava() 12 | hello.printHello() 13 | } 14 | 15 | /** 16 | * リスト3.2.1 17 | */ 18 | fun list3_2_1() { 19 | val uuid: UUID = UUID.randomUUID() 20 | println(uuid.toString()) 21 | } 22 | 23 | /** 24 | * リスト3.3.3 25 | */ 26 | fun list3_3_3() { 27 | val dog = DogKotlin() 28 | dog.cry() 29 | } 30 | 31 | /** 32 | * リスト3.3.6 33 | */ 34 | fun list3_3_6() { 35 | val greeter = GreeterImplKotlin() 36 | greeter.hello() 37 | } 38 | 39 | /** 40 | * リスト3.4.2 41 | */ 42 | fun list3_4_2() { 43 | val user = UserJava() 44 | user.id = 100 45 | user.name = "Takehata" 46 | println(user.id) 47 | println(user.name) 48 | } 49 | 50 | /** 51 | * リスト3.4.4 52 | */ 53 | fun list3_4_4() { 54 | val function = CalcJava { num1, num2 -> num1 + num2 } 55 | println(function.calc(1, 3)) 56 | } 57 | 58 | /** 59 | * リスト3.4.5 60 | */ 61 | fun executeCalc(num1: Int, num2: Int, function: CalcJava) { 62 | println(function.calc(num1, num2)) 63 | } 64 | 65 | fun list3_4_5() { 66 | executeCalc(1, 3, CalcJava { num1, num2 -> num1 + num2 }) 67 | val function = CalcJava { num1, num2 -> num1 + num2 } 68 | } 69 | 70 | -------------------------------------------------------------------------------- /part1/chapter03/src/main/kotlin/Time.kt: -------------------------------------------------------------------------------- 1 | import java.time.LocalDateTime 2 | 3 | /** 4 | * リスト3.2.2 5 | */ 6 | data class Time(val time: LocalDateTime) 7 | fun main() { 8 | val now = Time(LocalDateTime.now()) 9 | println(now.time) 10 | } -------------------------------------------------------------------------------- /part2/book-manager/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/** 6 | !**/src/test/** 7 | cookie.txt 8 | 9 | ### STS ### 10 | .apt_generated 11 | .classpath 12 | .factorypath 13 | .project 14 | .settings 15 | .springBeans 16 | .sts4-cache 17 | 18 | ### IntelliJ IDEA ### 19 | .idea 20 | *.iws 21 | *.iml 22 | *.ipr 23 | out/ 24 | 25 | ### NetBeans ### 26 | /nbproject/private/ 27 | /nbbuild/ 28 | /dist/ 29 | /nbdist/ 30 | /.nb-gradle/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | 35 | ### docker ### 36 | docker/db/** 37 | docker/redis/** 38 | -------------------------------------------------------------------------------- /part2/book-manager/README.md: -------------------------------------------------------------------------------- 1 | # 概要 2 | book-managerディレクトリが、Spring BootアプリケーションのGradleプロジェクトとなっています。 3 | IntelliJ IDEAで [File]->[Open] を選択し、このディレクトリのbuild.gradle.ktsを開いてください。 4 | ダイアログが表示されるので、[Open as Project] を選択してください。 5 | 6 | 6〜8章で作成した内容が全て入った状態のものになっています。 7 | 8 | # 実行方法 9 | 書籍内で解説しているものと同様、以下の方法で実行できます。 10 | 11 | - IntelliJ IDEAのGradleビューから、[Tasks]->[application]->[bootRun]を実行 12 | - ターミナルでdemoディレクトリから ```./gradlew bootRun``` を実行 13 | - DemoApplication.ktのmain関数を実行 14 | 15 | # ハッシュ化したパスワードの生成方法 16 | P153に記載されている、bcryptのアルゴリズムでハッシュ化したパスワードを生成する方法になります。 17 | `htpasswd`コマンドを使用して以下のような形で生成できます。 18 | 19 | ```zsh 20 | $ htpasswd -n -B pass 21 | New password: #引数で渡したパスワードを入力 22 | Re-type new password: #引数で渡したパスワードを入力 23 | pass:$2y$05$7IWY4KIY8l52R5XbTfU24uRw679eZeHHiSwtY2sfT5R7eAVGdJ1IS # この値を使用する 24 | ``` 25 | 26 | `-n`オプションで出力先を標準出力にし、-Bオプションでbcryptのアルゴリズムを使用するように指定しています。 27 | これで最後の引数(上記の例ではpass)をbcryptでハッシュ化した値が標準出力に表示されます。 28 | その後2回パスワードの入力を求められるので、引数と同様の値を入力し、出力された値をデータベースに登録するパスワードとして使用します。 29 | 30 | # 補足 31 | 複数のリストで同じクラス名や関数名を使っている場合など、並べて書かれているとコンパイルエラーになってしまうものは、コメントアウトされています。 32 | 必要に応じてコメントを外し、実行してください。 33 | コンパイルエラーになる例を紹介しているコードに関しても、同様にコメントアウトされています。 -------------------------------------------------------------------------------- /part2/book-manager/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | plugins { 4 | id("org.springframework.boot") version "2.4.3" 5 | id("io.spring.dependency-management") version "1.0.11.RELEASE" 6 | id("com.arenagod.gradle.MybatisGenerator") version "1.4" // 追加 7 | kotlin("jvm") version "1.4.30" 8 | kotlin("plugin.spring") version "1.4.30" 9 | } 10 | 11 | group = "com.book.manager" 12 | version = "0.0.1-SNAPSHOT" 13 | java.sourceCompatibility = JavaVersion.VERSION_11 14 | 15 | repositories { 16 | mavenCentral() 17 | } 18 | 19 | dependencies { 20 | implementation("org.springframework.boot:spring-boot-starter-web") 21 | implementation("com.fasterxml.jackson.module:jackson-module-kotlin") 22 | implementation("org.jetbrains.kotlin:kotlin-reflect") 23 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") 24 | 25 | implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.4") 26 | implementation("org.mybatis.dynamic-sql:mybatis-dynamic-sql:1.2.1") // 追加 27 | implementation("mysql:mysql-connector-java:8.0.23") // 追加 28 | implementation("org.springframework.boot:spring-boot-starter-security") 29 | implementation("org.springframework.session:spring-session-data-redis") 30 | implementation("redis.clients:jedis") 31 | implementation("org.springframework.boot:spring-boot-starter-aop") 32 | mybatisGenerator("org.mybatis.generator:mybatis-generator-core:1.4.0") // 追加 33 | 34 | testImplementation("org.springframework.boot:spring-boot-starter-test") 35 | testImplementation("org.junit.jupiter:junit-jupiter-engine:5.7.1") 36 | testImplementation("org.assertj:assertj-core:3.19.0") 37 | testImplementation("org.mockito:mockito-core:3.8.0") 38 | testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0") 39 | } 40 | 41 | tasks.withType { 42 | useJUnitPlatform() 43 | } 44 | 45 | tasks.withType { 46 | kotlinOptions { 47 | freeCompilerArgs = listOf("-Xjsr305=strict") 48 | jvmTarget = "11" 49 | } 50 | } 51 | 52 | // 追加 53 | mybatisGenerator { 54 | verbose = true 55 | configFile = "${projectDir}/src/main/resources/generatorConfig.xml" 56 | } -------------------------------------------------------------------------------- /part2/book-manager/docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | # MySQL 4 | db: 5 | image: mysql:8.0.22 6 | ports: 7 | - "3306:3306" 8 | container_name: mysql_host 9 | environment: 10 | MYSQL_ROOT_PASSWORD: mysql 11 | command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci 12 | volumes: 13 | - ./db/data:/var/lib/mysql 14 | - ./db/my.cnf:/etc/mysql/conf.d/my.cnf 15 | - ./db/sql:/docker-entrypoint-initdb.d 16 | # Redis 17 | redis: 18 | image: "redis:latest" 19 | ports: 20 | - "6379:6379" 21 | volumes: 22 | - "./redis:/data" -------------------------------------------------------------------------------- /part2/book-manager/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-takehata/kotlin-server-side-programming-practice/3526abaf27718941f5d36bd2588ec126e9386ae5/part2/book-manager/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part2/book-manager/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part2/book-manager/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven("https://dl.bintray.com/kotlin/kotlin-eap") 4 | 5 | mavenCentral() 6 | 7 | maven("https://plugins.gradle.org/m2/") 8 | } 9 | } 10 | rootProject.name = "book-manager" 11 | -------------------------------------------------------------------------------- /part2/book-manager/sql/book_manager.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE user ( 2 | id bigint NOT NULL, 3 | email varchar(256) UNIQUE NOT NULL, 4 | password varchar(128) NOT NULL, 5 | name varchar(32) NOT NULL, 6 | role_type enum('ADMIN', 'USER'), 7 | PRIMARY KEY (id) 8 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 9 | 10 | CREATE TABLE book ( 11 | id bigint NOT NULL, 12 | title varchar(128) NOT NULL, 13 | author varchar(32) NOT NULL, 14 | release_date date NOT NULL, 15 | PRIMARY KEY (id) 16 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 17 | 18 | CREATE TABLE rental ( 19 | book_id bigint NOT NULL, 20 | user_id bigint NOT NULL, 21 | rental_datetime datetime NOT NULL, 22 | return_deadline datetime NOT NULL, 23 | PRIMARY KEY (book_id) 24 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 25 | -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/BookManagerApplication.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication 4 | import org.springframework.boot.runApplication 5 | 6 | @SpringBootApplication 7 | class BookManagerApplication 8 | 9 | fun main(args: Array) { 10 | runApplication(*args) 11 | } 12 | -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/application/service/AdminBookService.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.application.service 2 | 3 | import com.book.manager.domain.model.Book 4 | import com.book.manager.domain.repository.BookRepository 5 | import org.springframework.stereotype.Service 6 | import org.springframework.transaction.annotation.Transactional 7 | import java.time.LocalDate 8 | 9 | @Service 10 | class AdminBookService( 11 | private val bookRepository: BookRepository 12 | ) { 13 | @Transactional 14 | fun register(book: Book) { 15 | bookRepository.findWithRental(book.id)?.let { throw IllegalArgumentException("既に存在する書籍ID: ${book.id}") } 16 | bookRepository.register(book) 17 | } 18 | 19 | @Transactional 20 | fun update(bookId: Long, title: String?, author: String?, releaseDate: LocalDate?) { 21 | bookRepository.findWithRental(bookId) ?: throw IllegalArgumentException("存在しない書籍ID: $bookId") 22 | bookRepository.update(bookId, title, author, releaseDate) 23 | } 24 | 25 | @Transactional 26 | fun delete(bookId: Long) { 27 | bookRepository.findWithRental(bookId) ?: throw IllegalArgumentException("存在しない書籍ID: $bookId") 28 | bookRepository.delete(bookId) 29 | } 30 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/application/service/AuthenticationService.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.application.service 2 | 3 | import com.book.manager.domain.model.User 4 | import com.book.manager.domain.repository.UserRepository 5 | import org.springframework.stereotype.Service 6 | 7 | @Service 8 | class AuthenticationService(private val userRepository: UserRepository) { 9 | fun findUser(email: String): User? { 10 | return userRepository.find(email) 11 | } 12 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/application/service/BookService.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.application.service 2 | 3 | import com.book.manager.domain.model.BookWithRental 4 | import com.book.manager.domain.repository.BookRepository 5 | import org.springframework.stereotype.Service 6 | 7 | @Service 8 | class BookService( 9 | private val bookRepository: BookRepository 10 | ) { 11 | fun getList(): List { 12 | return bookRepository.findAllWithRental() 13 | } 14 | 15 | fun getDetail(bookId: Long): BookWithRental { 16 | return bookRepository.findWithRental(bookId) ?: throw IllegalArgumentException("存在しない書籍ID: $bookId") 17 | } 18 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/application/service/RentalService.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.application.service 2 | 3 | import com.book.manager.domain.model.Rental 4 | import com.book.manager.domain.repository.BookRepository 5 | import com.book.manager.domain.repository.RentalRepository 6 | import com.book.manager.domain.repository.UserRepository 7 | import org.springframework.stereotype.Service 8 | import org.springframework.transaction.annotation.Transactional 9 | import java.time.LocalDateTime 10 | 11 | // 貸出期間 12 | private const val RENTAL_TERM_DAYS = 14L 13 | 14 | @Service 15 | class RentalService( 16 | private val userRepository: UserRepository, 17 | private val bookRepository: BookRepository, 18 | private val rentalRepository: RentalRepository, 19 | ) { 20 | @Transactional 21 | fun startRental(bookId: Long, userId: Long) { 22 | userRepository.find(userId) ?: throw IllegalArgumentException("該当するユーザーが存在しません userId:${userId}") 23 | val book = 24 | bookRepository.findWithRental(bookId) ?: throw IllegalArgumentException("該当する書籍が存在しません bookId:${bookId}") 25 | 26 | // 貸出中のチェック 27 | if (book.isRental) throw IllegalStateException("貸出中の商品です bookId:${bookId}") 28 | 29 | val rentalDateTime = LocalDateTime.now() 30 | val returnDeadline = rentalDateTime.plusDays(RENTAL_TERM_DAYS) 31 | val rental = Rental(bookId, userId, rentalDateTime, returnDeadline) 32 | 33 | rentalRepository.startRental(rental) 34 | } 35 | 36 | @Transactional 37 | fun endRental(bookId: Long, userId: Long) { 38 | userRepository.find(userId) ?: throw IllegalArgumentException("該当するユーザーが存在しません userId:${userId}") 39 | val book = 40 | bookRepository.findWithRental(bookId) ?: throw IllegalArgumentException("該当する書籍が存在しません bookId:${bookId}") 41 | 42 | // 貸出中のチェック 43 | if (!book.isRental) throw IllegalStateException("未貸出の商品です bookId:${bookId}") 44 | if (book.rental!!.userId != userId) throw IllegalStateException("他のユーザーが貸出中の商品です userId:${userId} bookId:${bookId}") 45 | 46 | rentalRepository.endRental(bookId) 47 | } 48 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/application/service/security/BookManagerUserDetailsService.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.application.service.security 2 | 3 | import com.book.manager.application.service.AuthenticationService 4 | import com.book.manager.domain.enum.RoleType 5 | import com.book.manager.domain.model.User 6 | import org.springframework.security.core.GrantedAuthority 7 | import org.springframework.security.core.authority.AuthorityUtils 8 | import org.springframework.security.core.userdetails.UserDetails 9 | import org.springframework.security.core.userdetails.UserDetailsService 10 | 11 | class BookManagerUserDetailsService( 12 | private val authenticationService: AuthenticationService 13 | ) : UserDetailsService { 14 | override fun loadUserByUsername(username: String): UserDetails? { 15 | val user = authenticationService.findUser(username) 16 | return user?.let { BookManagerUserDetails(user) } 17 | } 18 | } 19 | 20 | data class BookManagerUserDetails(val id: Long, val email: String, val pass: String, val roleType: RoleType) : 21 | UserDetails { 22 | 23 | constructor(user: User) : this(user.id, user.email, user.password, user.roleType) 24 | 25 | override fun getAuthorities(): MutableCollection { 26 | return AuthorityUtils.createAuthorityList(this.roleType.toString()) 27 | } 28 | 29 | override fun isEnabled(): Boolean { 30 | return true 31 | } 32 | 33 | override fun getUsername(): String { 34 | return this.email 35 | } 36 | 37 | override fun isCredentialsNonExpired(): Boolean { 38 | return true 39 | } 40 | 41 | override fun getPassword(): String { 42 | return this.pass 43 | } 44 | 45 | override fun isAccountNonExpired(): Boolean { 46 | return true 47 | } 48 | 49 | override fun isAccountNonLocked(): Boolean { 50 | return true 51 | } 52 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/domain/enum/RoleType.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.domain.enum 2 | 3 | enum class RoleType { ADMIN, USER } 4 | -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/domain/model/Book.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.domain.model 2 | 3 | import java.time.LocalDate 4 | 5 | data class Book( 6 | val id: Long, 7 | val title: String, 8 | val author: String, 9 | val releaseDate: LocalDate 10 | ) -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/domain/model/BookWithRental.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.domain.model 2 | 3 | data class BookWithRental( 4 | val book: Book, 5 | val rental: Rental? 6 | ) { 7 | val isRental: Boolean 8 | get() = rental != null 9 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/domain/model/Rental.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.domain.model 2 | 3 | import java.time.LocalDateTime 4 | 5 | data class Rental( 6 | val bookId: Long, 7 | val userId: Long, 8 | val rentalDatetime: LocalDateTime, 9 | val returnDeadline: LocalDateTime 10 | ) -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/domain/model/User.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.domain.model 2 | 3 | import com.book.manager.domain.enum.RoleType 4 | 5 | data class User( 6 | val id: Long, 7 | val email: String, 8 | val password: String, 9 | val name: String, 10 | val roleType: RoleType 11 | ) -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/domain/repository/BookRepository.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.domain.repository 2 | 3 | import com.book.manager.domain.model.Book 4 | import com.book.manager.domain.model.BookWithRental 5 | import java.time.LocalDate 6 | 7 | interface BookRepository { 8 | fun findAllWithRental(): List 9 | fun findWithRental(id: Long): BookWithRental? 10 | fun register(book: Book) 11 | fun update(id: Long, title: String?, author: String?, releaseDate: LocalDate?) 12 | fun delete(id: Long) 13 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/domain/repository/RentalRepository.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.domain.repository 2 | 3 | import com.book.manager.domain.model.Rental 4 | 5 | interface RentalRepository { 6 | fun startRental(rental: Rental) 7 | fun endRental(bookId: Long) 8 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/domain/repository/UserRepository.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.domain.repository 2 | 3 | import com.book.manager.domain.model.User 4 | 5 | interface UserRepository { 6 | fun find(email: String): User? 7 | fun find(id: Long): User? 8 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/infrastructure/database/mapper/BookDynamicSqlSupport.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Auto-generated file. Created by MyBatis Generator 3 | */ 4 | package com.book.manager.infrastructure.database.mapper 5 | 6 | import java.sql.JDBCType 7 | import java.time.LocalDate 8 | import org.mybatis.dynamic.sql.SqlTable 9 | 10 | object BookDynamicSqlSupport { 11 | object Book : SqlTable("book") { 12 | val id = column("id", JDBCType.BIGINT) 13 | 14 | val title = column("title", JDBCType.VARCHAR) 15 | 16 | val author = column("author", JDBCType.VARCHAR) 17 | 18 | val releaseDate = column("release_date", JDBCType.DATE) 19 | } 20 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/infrastructure/database/mapper/BookMapper.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Auto-generated file. Created by MyBatis Generator 3 | */ 4 | package com.book.manager.infrastructure.database.mapper 5 | 6 | import com.book.manager.infrastructure.database.record.BookRecord 7 | import org.apache.ibatis.annotations.DeleteProvider 8 | import org.apache.ibatis.annotations.InsertProvider 9 | import org.apache.ibatis.annotations.Mapper 10 | import org.apache.ibatis.annotations.Result 11 | import org.apache.ibatis.annotations.ResultMap 12 | import org.apache.ibatis.annotations.Results 13 | import org.apache.ibatis.annotations.SelectProvider 14 | import org.apache.ibatis.annotations.UpdateProvider 15 | import org.apache.ibatis.type.JdbcType 16 | import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider 17 | import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider 18 | import org.mybatis.dynamic.sql.insert.render.MultiRowInsertStatementProvider 19 | import org.mybatis.dynamic.sql.select.render.SelectStatementProvider 20 | import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider 21 | import org.mybatis.dynamic.sql.util.SqlProviderAdapter 22 | 23 | @Mapper 24 | interface BookMapper { 25 | @SelectProvider(type=SqlProviderAdapter::class, method="select") 26 | fun count(selectStatement: SelectStatementProvider): Long 27 | 28 | @DeleteProvider(type=SqlProviderAdapter::class, method="delete") 29 | fun delete(deleteStatement: DeleteStatementProvider): Int 30 | 31 | @InsertProvider(type=SqlProviderAdapter::class, method="insert") 32 | fun insert(insertStatement: InsertStatementProvider): Int 33 | 34 | @InsertProvider(type=SqlProviderAdapter::class, method="insertMultiple") 35 | fun insertMultiple(multipleInsertStatement: MultiRowInsertStatementProvider): Int 36 | 37 | @SelectProvider(type=SqlProviderAdapter::class, method="select") 38 | @ResultMap("BookRecordResult") 39 | fun selectOne(selectStatement: SelectStatementProvider): BookRecord? 40 | 41 | @SelectProvider(type=SqlProviderAdapter::class, method="select") 42 | @Results(id="BookRecordResult", value = [ 43 | Result(column="id", property="id", jdbcType=JdbcType.BIGINT, id=true), 44 | Result(column="title", property="title", jdbcType=JdbcType.VARCHAR), 45 | Result(column="author", property="author", jdbcType=JdbcType.VARCHAR), 46 | Result(column="release_date", property="releaseDate", jdbcType=JdbcType.DATE) 47 | ]) 48 | fun selectMany(selectStatement: SelectStatementProvider): List 49 | 50 | @UpdateProvider(type=SqlProviderAdapter::class, method="update") 51 | fun update(updateStatement: UpdateStatementProvider): Int 52 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/infrastructure/database/mapper/RentalDynamicSqlSupport.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Auto-generated file. Created by MyBatis Generator 3 | */ 4 | package com.book.manager.infrastructure.database.mapper 5 | 6 | import java.sql.JDBCType 7 | import java.time.LocalDateTime 8 | import org.mybatis.dynamic.sql.SqlTable 9 | 10 | object RentalDynamicSqlSupport { 11 | object Rental : SqlTable("rental") { 12 | val bookId = column("book_id", JDBCType.BIGINT) 13 | 14 | val userId = column("user_id", JDBCType.BIGINT) 15 | 16 | val rentalDatetime = column("rental_datetime", JDBCType.TIMESTAMP) 17 | 18 | val returnDeadline = column("return_deadline", JDBCType.TIMESTAMP) 19 | } 20 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/infrastructure/database/mapper/RentalMapper.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Auto-generated file. Created by MyBatis Generator 3 | */ 4 | package com.book.manager.infrastructure.database.mapper 5 | 6 | import com.book.manager.infrastructure.database.record.RentalRecord 7 | import org.apache.ibatis.annotations.DeleteProvider 8 | import org.apache.ibatis.annotations.InsertProvider 9 | import org.apache.ibatis.annotations.Mapper 10 | import org.apache.ibatis.annotations.Result 11 | import org.apache.ibatis.annotations.ResultMap 12 | import org.apache.ibatis.annotations.Results 13 | import org.apache.ibatis.annotations.SelectProvider 14 | import org.apache.ibatis.annotations.UpdateProvider 15 | import org.apache.ibatis.type.JdbcType 16 | import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider 17 | import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider 18 | import org.mybatis.dynamic.sql.insert.render.MultiRowInsertStatementProvider 19 | import org.mybatis.dynamic.sql.select.render.SelectStatementProvider 20 | import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider 21 | import org.mybatis.dynamic.sql.util.SqlProviderAdapter 22 | 23 | @Mapper 24 | interface RentalMapper { 25 | @SelectProvider(type=SqlProviderAdapter::class, method="select") 26 | fun count(selectStatement: SelectStatementProvider): Long 27 | 28 | @DeleteProvider(type=SqlProviderAdapter::class, method="delete") 29 | fun delete(deleteStatement: DeleteStatementProvider): Int 30 | 31 | @InsertProvider(type=SqlProviderAdapter::class, method="insert") 32 | fun insert(insertStatement: InsertStatementProvider): Int 33 | 34 | @InsertProvider(type=SqlProviderAdapter::class, method="insertMultiple") 35 | fun insertMultiple(multipleInsertStatement: MultiRowInsertStatementProvider): Int 36 | 37 | @SelectProvider(type=SqlProviderAdapter::class, method="select") 38 | @ResultMap("RentalRecordResult") 39 | fun selectOne(selectStatement: SelectStatementProvider): RentalRecord? 40 | 41 | @SelectProvider(type=SqlProviderAdapter::class, method="select") 42 | @Results(id="RentalRecordResult", value = [ 43 | Result(column="book_id", property="bookId", jdbcType=JdbcType.BIGINT, id=true), 44 | Result(column="user_id", property="userId", jdbcType=JdbcType.BIGINT), 45 | Result(column="rental_datetime", property="rentalDatetime", jdbcType=JdbcType.TIMESTAMP), 46 | Result(column="return_deadline", property="returnDeadline", jdbcType=JdbcType.TIMESTAMP) 47 | ]) 48 | fun selectMany(selectStatement: SelectStatementProvider): List 49 | 50 | @UpdateProvider(type=SqlProviderAdapter::class, method="update") 51 | fun update(updateStatement: UpdateStatementProvider): Int 52 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/infrastructure/database/mapper/UserDynamicSqlSupport.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Auto-generated file. Created by MyBatis Generator 3 | */ 4 | package com.book.manager.infrastructure.database.mapper 5 | 6 | import com.book.manager.domain.enum.RoleType 7 | import java.sql.JDBCType 8 | import org.mybatis.dynamic.sql.SqlTable 9 | 10 | object UserDynamicSqlSupport { 11 | object User : SqlTable("user") { 12 | val id = column("id", JDBCType.BIGINT) 13 | 14 | val email = column("email", JDBCType.VARCHAR) 15 | 16 | val password = column("password", JDBCType.VARCHAR) 17 | 18 | val name = column("name", JDBCType.VARCHAR) 19 | 20 | val roleType = column("role_type", JDBCType.CHAR, "org.apache.ibatis.type.EnumTypeHandler") 21 | } 22 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/infrastructure/database/mapper/UserMapper.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Auto-generated file. Created by MyBatis Generator 3 | */ 4 | package com.book.manager.infrastructure.database.mapper 5 | 6 | import com.book.manager.infrastructure.database.record.UserRecord 7 | import org.apache.ibatis.annotations.DeleteProvider 8 | import org.apache.ibatis.annotations.InsertProvider 9 | import org.apache.ibatis.annotations.Mapper 10 | import org.apache.ibatis.annotations.Result 11 | import org.apache.ibatis.annotations.ResultMap 12 | import org.apache.ibatis.annotations.Results 13 | import org.apache.ibatis.annotations.SelectProvider 14 | import org.apache.ibatis.annotations.UpdateProvider 15 | import org.apache.ibatis.type.EnumTypeHandler 16 | import org.apache.ibatis.type.JdbcType 17 | import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider 18 | import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider 19 | import org.mybatis.dynamic.sql.insert.render.MultiRowInsertStatementProvider 20 | import org.mybatis.dynamic.sql.select.render.SelectStatementProvider 21 | import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider 22 | import org.mybatis.dynamic.sql.util.SqlProviderAdapter 23 | 24 | @Mapper 25 | interface UserMapper { 26 | @SelectProvider(type=SqlProviderAdapter::class, method="select") 27 | fun count(selectStatement: SelectStatementProvider): Long 28 | 29 | @DeleteProvider(type=SqlProviderAdapter::class, method="delete") 30 | fun delete(deleteStatement: DeleteStatementProvider): Int 31 | 32 | @InsertProvider(type=SqlProviderAdapter::class, method="insert") 33 | fun insert(insertStatement: InsertStatementProvider): Int 34 | 35 | @InsertProvider(type=SqlProviderAdapter::class, method="insertMultiple") 36 | fun insertMultiple(multipleInsertStatement: MultiRowInsertStatementProvider): Int 37 | 38 | @SelectProvider(type=SqlProviderAdapter::class, method="select") 39 | @ResultMap("UserRecordResult") 40 | fun selectOne(selectStatement: SelectStatementProvider): UserRecord? 41 | 42 | @SelectProvider(type=SqlProviderAdapter::class, method="select") 43 | @Results(id="UserRecordResult", value = [ 44 | Result(column="id", property="id", jdbcType=JdbcType.BIGINT, id=true), 45 | Result(column="email", property="email", jdbcType=JdbcType.VARCHAR), 46 | Result(column="password", property="password", jdbcType=JdbcType.VARCHAR), 47 | Result(column="name", property="name", jdbcType=JdbcType.VARCHAR), 48 | Result(column="role_type", property="roleType", typeHandler=EnumTypeHandler::class, jdbcType=JdbcType.CHAR) 49 | ]) 50 | fun selectMany(selectStatement: SelectStatementProvider): List 51 | 52 | @UpdateProvider(type=SqlProviderAdapter::class, method="update") 53 | fun update(updateStatement: UpdateStatementProvider): Int 54 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/infrastructure/database/mapper/custom/BookWithRentalMapper.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.infrastructure.database.mapper.custom 2 | 3 | import com.book.manager.infrastructure.database.record.custom.BookWithRentalRecord 4 | import org.apache.ibatis.annotations.Mapper 5 | import org.apache.ibatis.annotations.Result 6 | import org.apache.ibatis.annotations.ResultMap 7 | import org.apache.ibatis.annotations.Results 8 | import org.apache.ibatis.annotations.SelectProvider 9 | import org.apache.ibatis.type.JdbcType 10 | import org.mybatis.dynamic.sql.select.render.SelectStatementProvider 11 | import org.mybatis.dynamic.sql.util.SqlProviderAdapter 12 | 13 | @Mapper 14 | interface BookWithRentalMapper { 15 | @SelectProvider(type = SqlProviderAdapter::class, method = "select") 16 | @Results( 17 | id = "BookWithRentalRecordResult", value = [ 18 | Result(column = "id", property = "id", jdbcType = JdbcType.BIGINT, id = true), 19 | Result(column = "title", property = "title", jdbcType = JdbcType.VARCHAR), 20 | Result(column = "author", property = "author", jdbcType = JdbcType.VARCHAR), 21 | Result(column = "release_date", property = "releaseDate", jdbcType = JdbcType.DATE), 22 | Result(column = "user_id", property = "userId", jdbcType = JdbcType.BIGINT), 23 | Result(column = "rental_datetime", property = "rentalDatetime", jdbcType = JdbcType.TIMESTAMP), 24 | Result(column = "return_deadline", property = "returnDeadline", jdbcType = JdbcType.TIMESTAMP) 25 | ] 26 | ) 27 | fun selectMany(selectStatement: SelectStatementProvider): List 28 | 29 | @SelectProvider(type = SqlProviderAdapter::class, method = "select") 30 | @ResultMap("BookWithRentalRecordResult") 31 | fun selectOne(selectStatement: SelectStatementProvider): BookWithRentalRecord? 32 | } 33 | -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/infrastructure/database/mapper/custom/BookWithRentalMapperExtentions.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.infrastructure.database.mapper.custom 2 | 3 | import com.book.manager.infrastructure.database.mapper.BookDynamicSqlSupport.Book 4 | import com.book.manager.infrastructure.database.mapper.BookDynamicSqlSupport.Book.author 5 | import com.book.manager.infrastructure.database.mapper.BookDynamicSqlSupport.Book.id 6 | import com.book.manager.infrastructure.database.mapper.BookDynamicSqlSupport.Book.releaseDate 7 | import com.book.manager.infrastructure.database.mapper.BookDynamicSqlSupport.Book.title 8 | import com.book.manager.infrastructure.database.mapper.RentalDynamicSqlSupport.Rental 9 | import com.book.manager.infrastructure.database.mapper.RentalDynamicSqlSupport.Rental.rentalDatetime 10 | import com.book.manager.infrastructure.database.mapper.RentalDynamicSqlSupport.Rental.returnDeadline 11 | import com.book.manager.infrastructure.database.mapper.RentalDynamicSqlSupport.Rental.userId 12 | import com.book.manager.infrastructure.database.record.custom.BookWithRentalRecord 13 | import org.mybatis.dynamic.sql.SqlBuilder.equalTo 14 | import org.mybatis.dynamic.sql.SqlBuilder.isEqualTo 15 | import org.mybatis.dynamic.sql.SqlBuilder.select 16 | import org.mybatis.dynamic.sql.util.kotlin.mybatis3.from 17 | 18 | private val columnList = listOf( 19 | id, 20 | title, 21 | author, 22 | releaseDate, 23 | userId, 24 | rentalDatetime, 25 | returnDeadline 26 | ) 27 | 28 | fun BookWithRentalMapper.select(): List { 29 | val selectStatement = select(columnList).from(Book, "b") { 30 | leftJoin(Rental, "r") { 31 | on(Book.id, equalTo(Rental.bookId)) 32 | } 33 | } 34 | return selectMany(selectStatement) 35 | } 36 | 37 | fun BookWithRentalMapper.selectByPrimaryKey(id_: Long): BookWithRentalRecord? { 38 | val selectStatement = select(columnList).from(Book, "b") { 39 | leftJoin(Rental, "r") { 40 | on(Book.id, equalTo(Rental.bookId)) 41 | } 42 | where(id, isEqualTo(id_)) 43 | } 44 | return selectOne(selectStatement) 45 | } 46 | -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/infrastructure/database/record/BookRecord.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Auto-generated file. Created by MyBatis Generator 3 | */ 4 | package com.book.manager.infrastructure.database.record 5 | 6 | import java.time.LocalDate 7 | 8 | data class BookRecord( 9 | var id: Long? = null, 10 | var title: String? = null, 11 | var author: String? = null, 12 | var releaseDate: LocalDate? = null 13 | ) -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/infrastructure/database/record/RentalRecord.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Auto-generated file. Created by MyBatis Generator 3 | */ 4 | package com.book.manager.infrastructure.database.record 5 | 6 | import java.time.LocalDateTime 7 | 8 | data class RentalRecord( 9 | var bookId: Long? = null, 10 | var userId: Long? = null, 11 | var rentalDatetime: LocalDateTime? = null, 12 | var returnDeadline: LocalDateTime? = null 13 | ) -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/infrastructure/database/record/UserRecord.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Auto-generated file. Created by MyBatis Generator 3 | */ 4 | package com.book.manager.infrastructure.database.record 5 | 6 | import com.book.manager.domain.enum.RoleType 7 | 8 | data class UserRecord( 9 | var id: Long? = null, 10 | var email: String? = null, 11 | var password: String? = null, 12 | var name: String? = null, 13 | var roleType: RoleType? = null 14 | ) -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/infrastructure/database/record/custom/BookWithRentalRecord.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Auto-generated file. Created by MyBatis Generator 3 | */ 4 | package com.book.manager.infrastructure.database.record.custom 5 | 6 | import java.time.LocalDate 7 | import java.time.LocalDateTime 8 | 9 | data class BookWithRentalRecord( 10 | var id: Long? = null, 11 | var title: String? = null, 12 | var author: String? = null, 13 | var releaseDate: LocalDate? = null, 14 | var userId: Long? = null, 15 | var rentalDatetime: LocalDateTime? = null, 16 | var returnDeadline: LocalDateTime? = null 17 | ) -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/infrastructure/database/repository/BookRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.infrastructure.database.repository 2 | 3 | import com.book.manager.domain.model.Book 4 | import com.book.manager.domain.model.BookWithRental 5 | import com.book.manager.domain.model.Rental 6 | import com.book.manager.domain.repository.BookRepository 7 | import com.book.manager.infrastructure.database.mapper.BookMapper 8 | import com.book.manager.infrastructure.database.mapper.custom.BookWithRentalMapper 9 | import com.book.manager.infrastructure.database.mapper.custom.select 10 | import com.book.manager.infrastructure.database.mapper.custom.selectByPrimaryKey 11 | import com.book.manager.infrastructure.database.mapper.deleteByPrimaryKey 12 | import com.book.manager.infrastructure.database.mapper.insert 13 | import com.book.manager.infrastructure.database.mapper.updateByPrimaryKeySelective 14 | import com.book.manager.infrastructure.database.record.BookRecord 15 | import com.book.manager.infrastructure.database.record.custom.BookWithRentalRecord 16 | import org.springframework.stereotype.Repository 17 | import java.time.LocalDate 18 | 19 | @Suppress("SpringJavaInjectionPointsAutowiringInspection") 20 | @Repository 21 | class BookRepositoryImpl( 22 | private val bookWithRentalMapper: BookWithRentalMapper, 23 | private val bookMapper: BookMapper 24 | ) : BookRepository { 25 | override fun findAllWithRental(): List { 26 | return bookWithRentalMapper.select().map { toModel(it) } 27 | } 28 | 29 | override fun findWithRental(id: Long): BookWithRental? { 30 | return bookWithRentalMapper.selectByPrimaryKey(id)?.let { toModel(it) } 31 | } 32 | 33 | override fun register(book: Book) { 34 | bookMapper.insert(toRecord(book)) 35 | } 36 | 37 | override fun update(id: Long, title: String?, author: String?, releaseDate: LocalDate?) { 38 | bookMapper.updateByPrimaryKeySelective(BookRecord(id, title, author, releaseDate)) 39 | } 40 | 41 | override fun delete(id: Long) { 42 | bookMapper.deleteByPrimaryKey(id) 43 | } 44 | 45 | private fun toModel(record: BookWithRentalRecord): BookWithRental { 46 | val book = Book( 47 | record.id!!, 48 | record.title!!, 49 | record.author!!, 50 | record.releaseDate!! 51 | ) 52 | val rental = record.userId?.let { 53 | Rental( 54 | record.id!!, 55 | record.userId!!, 56 | record.rentalDatetime!!, 57 | record.returnDeadline!! 58 | ) 59 | } 60 | return BookWithRental(book, rental) 61 | } 62 | 63 | private fun toRecord(model: Book): BookRecord { 64 | return BookRecord(model.id, model.title, model.author, model.releaseDate) 65 | } 66 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/infrastructure/database/repository/RentalRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.infrastructure.database.repository 2 | 3 | import com.book.manager.domain.model.Rental 4 | import com.book.manager.domain.repository.RentalRepository 5 | import com.book.manager.infrastructure.database.mapper.RentalMapper 6 | import com.book.manager.infrastructure.database.mapper.deleteByPrimaryKey 7 | import com.book.manager.infrastructure.database.mapper.insert 8 | import com.book.manager.infrastructure.database.record.RentalRecord 9 | import org.springframework.stereotype.Repository 10 | 11 | @Suppress("SpringJavaInjectionPointsAutowiringInspection") 12 | @Repository 13 | class RentalRepositoryImpl( 14 | private val rentalMapper: RentalMapper 15 | ) : RentalRepository { 16 | override fun startRental(rental: Rental) { 17 | rentalMapper.insert(toRecord(rental)) 18 | } 19 | 20 | override fun endRental(bookId: Long) { 21 | rentalMapper.deleteByPrimaryKey(bookId) 22 | } 23 | 24 | private fun toRecord(model: Rental): RentalRecord { 25 | return RentalRecord(model.bookId, model.userId, model.rentalDatetime, model.returnDeadline) 26 | } 27 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/infrastructure/database/repository/UserRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.infrastructure.database.repository 2 | 3 | import com.book.manager.domain.model.User 4 | import com.book.manager.domain.repository.UserRepository 5 | import com.book.manager.infrastructure.database.mapper.UserDynamicSqlSupport 6 | import com.book.manager.infrastructure.database.mapper.UserMapper 7 | import com.book.manager.infrastructure.database.mapper.selectByPrimaryKey 8 | import com.book.manager.infrastructure.database.mapper.selectOne 9 | import com.book.manager.infrastructure.database.record.UserRecord 10 | import org.mybatis.dynamic.sql.SqlBuilder.isEqualTo 11 | import org.springframework.stereotype.Repository 12 | 13 | @Suppress("SpringJavaInjectionPointsAutowiringInspection") 14 | @Repository 15 | class UserRepositoryImpl( 16 | private val mapper: UserMapper 17 | ) : UserRepository { 18 | override fun find(email: String): User? { 19 | val record = mapper.selectOne { 20 | where(UserDynamicSqlSupport.User.email, isEqualTo(email)) 21 | } 22 | return record?.let { toModel(it) } 23 | } 24 | 25 | override fun find(id: Long): User? { 26 | val record = mapper.selectByPrimaryKey(id) 27 | return record?.let { toModel(it) } 28 | } 29 | 30 | private fun toModel(record: UserRecord): User { 31 | return User( 32 | record.id!!, 33 | record.email!!, 34 | record.password!!, 35 | record.name!!, 36 | record.roleType!! 37 | ) 38 | } 39 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/presentation/aop/LoggingAdvice.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.presentation.aop 2 | 3 | import com.book.manager.application.service.security.BookManagerUserDetails 4 | import org.aspectj.lang.JoinPoint 5 | import org.aspectj.lang.ProceedingJoinPoint 6 | import org.aspectj.lang.annotation.After 7 | import org.aspectj.lang.annotation.AfterReturning 8 | import org.aspectj.lang.annotation.AfterThrowing 9 | import org.aspectj.lang.annotation.Around 10 | import org.aspectj.lang.annotation.Aspect 11 | import org.aspectj.lang.annotation.Before 12 | import org.slf4j.LoggerFactory 13 | import org.springframework.security.core.context.SecurityContextHolder 14 | import org.springframework.stereotype.Component 15 | import org.springframework.web.context.request.RequestContextHolder 16 | import org.springframework.web.context.request.ServletRequestAttributes 17 | 18 | private val logger = LoggerFactory.getLogger(LoggingAdvice::class.java) 19 | 20 | @Aspect 21 | @Component 22 | class LoggingAdvice { 23 | @Before("execution(* com.book.manager.presentation.controller..*.*(..))") 24 | fun beforeLog(joinPoint: JoinPoint) { 25 | val user = SecurityContextHolder.getContext().authentication.principal as BookManagerUserDetails 26 | logger.info("Start: ${joinPoint.signature} userId=${user.id}") 27 | logger.info("Class: ${joinPoint.target.javaClass}") 28 | logger.info("Session: ${(RequestContextHolder.getRequestAttributes() as ServletRequestAttributes).request.session.id}") 29 | } 30 | 31 | @After("execution(* com.book.manager.presentation.controller..*.*(..))") 32 | fun afterLog(joinPoint: JoinPoint) { 33 | val user = SecurityContextHolder.getContext().authentication.principal as BookManagerUserDetails 34 | logger.info("End: ${joinPoint.signature} userId=${user.id}") 35 | } 36 | 37 | @Around("execution(* com.book.manager.presentation.controller..*.*(..))") 38 | fun aroundLog(joinPoint: ProceedingJoinPoint): Any? { 39 | // 前処理 40 | val user = SecurityContextHolder.getContext().authentication.principal as BookManagerUserDetails 41 | logger.info("Start Proceed: ${joinPoint.signature} userId=${user.id}") 42 | 43 | // 本処理の実行 44 | val result = joinPoint.proceed() 45 | 46 | // 後処理 47 | logger.info("End Proceed: ${joinPoint.signature} userId=${user.id}") 48 | 49 | // 本処理の結果の返却 50 | return result 51 | } 52 | 53 | @AfterReturning("execution(* com.book.manager.presentation.controller..*.*(..))", returning = "returnValue") 54 | fun afterReturningLog(joinPoint: JoinPoint, returnValue: Any?) { 55 | logger.info("End: ${joinPoint.signature} returnValue=${returnValue}") 56 | } 57 | 58 | @AfterThrowing("execution(* com.book.manager.presentation.controller..*.*(..))", throwing = "e") 59 | fun afterThrowingLog(joinPoint: JoinPoint, e: Throwable) { 60 | logger.error("Exception: ${e.javaClass} signature=${joinPoint.signature} message=${e.message}") 61 | } 62 | 63 | @AfterThrowing("execution(* com.book.manager.presentation.controller..*.*(..))", throwing = "e") 64 | fun afterThrowingLog(joinPoint: JoinPoint, e: IllegalArgumentException) { 65 | logger.error("Exception: ${e.javaClass} signature=${joinPoint.signature} message=${e.message}") 66 | } 67 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/presentation/config/HttpSessionConfig.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.presentation.config 2 | 3 | import org.springframework.context.annotation.Bean 4 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory 5 | import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession 6 | 7 | @EnableRedisHttpSession 8 | class HttpSessionConfig { 9 | @Bean 10 | fun connectionFactory(): JedisConnectionFactory { 11 | // val redisStandaloneConfiguration = RedisStandaloneConfiguration().also { 12 | // it.hostName = "kotlin-redis" 13 | // it.port = 16379 14 | // } 15 | // return JedisConnectionFactory(redisStandaloneConfiguration) 16 | return JedisConnectionFactory() 17 | } 18 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/presentation/config/SecurityConfig.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.presentation.config 2 | 3 | import com.book.manager.application.service.AuthenticationService 4 | import com.book.manager.application.service.security.BookManagerUserDetailsService 5 | import com.book.manager.domain.enum.RoleType 6 | import com.book.manager.presentation.handler.BookManagerAccessDeniedHandler 7 | import com.book.manager.presentation.handler.BookManagerAuthenticationEntryPoint 8 | import com.book.manager.presentation.handler.BookManagerAuthenticationFailureHandler 9 | import com.book.manager.presentation.handler.BookManagerAuthenticationSuccessHandler 10 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder 11 | import org.springframework.security.config.annotation.web.builders.HttpSecurity 12 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity 13 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter 14 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder 15 | import org.springframework.web.cors.CorsConfiguration 16 | import org.springframework.web.cors.CorsConfigurationSource 17 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource 18 | 19 | @EnableWebSecurity 20 | class SecurityConfig(private val authenticationService: AuthenticationService) : WebSecurityConfigurerAdapter() { 21 | override fun configure(http: HttpSecurity) { 22 | http.authorizeRequests() 23 | .mvcMatchers("/login").permitAll() 24 | .mvcMatchers("/admin/**").hasAuthority(RoleType.ADMIN.toString()) 25 | .anyRequest().authenticated() 26 | .and() 27 | .csrf().disable() 28 | .formLogin() 29 | .loginProcessingUrl("/login") 30 | .usernameParameter("email") 31 | .passwordParameter("pass") 32 | .successHandler(BookManagerAuthenticationSuccessHandler()) 33 | .failureHandler(BookManagerAuthenticationFailureHandler()) 34 | .and() 35 | .exceptionHandling() 36 | .authenticationEntryPoint(BookManagerAuthenticationEntryPoint()) 37 | .accessDeniedHandler(BookManagerAccessDeniedHandler()) 38 | .and() 39 | .cors() 40 | .configurationSource(corsConfigurationSource()) 41 | } 42 | 43 | override fun configure(auth: AuthenticationManagerBuilder) { 44 | auth.userDetailsService(BookManagerUserDetailsService(authenticationService)) 45 | .passwordEncoder(BCryptPasswordEncoder()) 46 | } 47 | 48 | private fun corsConfigurationSource(): CorsConfigurationSource { 49 | val corsConfiguration = CorsConfiguration() 50 | corsConfiguration.addAllowedMethod(CorsConfiguration.ALL) 51 | corsConfiguration.addAllowedHeader(CorsConfiguration.ALL) 52 | corsConfiguration.addAllowedOrigin("http://localhost:8081") 53 | corsConfiguration.allowCredentials = true 54 | 55 | val corsConfigurationSource = UrlBasedCorsConfigurationSource() 56 | corsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration) 57 | 58 | return corsConfigurationSource 59 | } 60 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/presentation/controller/AdminBookController.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.presentation.controller 2 | 3 | import com.book.manager.application.service.AdminBookService 4 | import com.book.manager.domain.model.Book 5 | import com.book.manager.presentation.form.RegisterBookRequest 6 | import com.book.manager.presentation.form.UpdateBookRequest 7 | import org.springframework.web.bind.annotation.CrossOrigin 8 | import org.springframework.web.bind.annotation.DeleteMapping 9 | import org.springframework.web.bind.annotation.PathVariable 10 | import org.springframework.web.bind.annotation.PostMapping 11 | import org.springframework.web.bind.annotation.PutMapping 12 | import org.springframework.web.bind.annotation.RequestBody 13 | import org.springframework.web.bind.annotation.RequestMapping 14 | import org.springframework.web.bind.annotation.RestController 15 | 16 | @RestController 17 | @RequestMapping("admin/book") 18 | @CrossOrigin 19 | class AdminBookController( 20 | private val adminBookService: AdminBookService 21 | ) { 22 | @PostMapping("/register") 23 | fun register(@RequestBody request: RegisterBookRequest) { 24 | adminBookService.register( 25 | Book( 26 | request.id, 27 | request.title, 28 | request.author, 29 | request.releaseDate 30 | ) 31 | ) 32 | } 33 | 34 | @PutMapping("/update") 35 | fun update(@RequestBody request: UpdateBookRequest) { 36 | adminBookService.update(request.id, request.title, request.author, request.releaseDate) 37 | } 38 | 39 | @DeleteMapping("/delete/{book_id}") 40 | fun delete(@PathVariable("book_id") bookId: Long) { 41 | adminBookService.delete(bookId) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/presentation/controller/BookController.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.presentation.controller 2 | 3 | import com.book.manager.application.service.BookService 4 | import com.book.manager.presentation.form.BookInfo 5 | import com.book.manager.presentation.form.GetBookDetailResponse 6 | import com.book.manager.presentation.form.GetBookListResponse 7 | import org.springframework.web.bind.annotation.CrossOrigin 8 | import org.springframework.web.bind.annotation.GetMapping 9 | import org.springframework.web.bind.annotation.PathVariable 10 | import org.springframework.web.bind.annotation.RequestMapping 11 | import org.springframework.web.bind.annotation.RestController 12 | 13 | @RestController 14 | @RequestMapping("book") 15 | @CrossOrigin 16 | class BookController( 17 | private val bookService: BookService 18 | ) { 19 | @GetMapping("/list") 20 | fun getList(): GetBookListResponse { 21 | val bookList = bookService.getList().map { 22 | BookInfo(it) 23 | } 24 | return GetBookListResponse(bookList) 25 | } 26 | 27 | @GetMapping("/detail/{book_id}") 28 | fun getDetail(@PathVariable("book_id") bookId: Long): GetBookDetailResponse { 29 | val book = bookService.getDetail(bookId) 30 | return GetBookDetailResponse(book) 31 | } 32 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/presentation/controller/RentalController.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.presentation.controller 2 | 3 | import com.book.manager.application.service.RentalService 4 | import com.book.manager.application.service.security.BookManagerUserDetails 5 | import com.book.manager.presentation.form.RentalStartRequest 6 | import org.springframework.security.core.context.SecurityContextHolder 7 | import org.springframework.web.bind.annotation.CrossOrigin 8 | import org.springframework.web.bind.annotation.DeleteMapping 9 | import org.springframework.web.bind.annotation.PathVariable 10 | import org.springframework.web.bind.annotation.PostMapping 11 | import org.springframework.web.bind.annotation.RequestBody 12 | import org.springframework.web.bind.annotation.RequestMapping 13 | import org.springframework.web.bind.annotation.RestController 14 | 15 | @RestController 16 | @RequestMapping("rental") 17 | @CrossOrigin 18 | class RentalController( 19 | private val rentalService: RentalService 20 | ) { 21 | @PostMapping("/start") 22 | fun startRental(@RequestBody request: RentalStartRequest) { 23 | val user = SecurityContextHolder.getContext().authentication.principal as BookManagerUserDetails 24 | rentalService.startRental(request.bookId, user.id) 25 | } 26 | 27 | @DeleteMapping("/end/{book_id}") 28 | fun endRental(@PathVariable("book_id") bookId: Long) { 29 | val user = SecurityContextHolder.getContext().authentication.principal as BookManagerUserDetails 30 | rentalService.endRental(bookId, user.id) 31 | } 32 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/presentation/form/BookForm.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.presentation.form 2 | 3 | import com.book.manager.domain.model.BookWithRental 4 | import com.book.manager.domain.model.Rental 5 | import java.time.LocalDate 6 | import java.time.LocalDateTime 7 | 8 | data class GetBookListResponse(val bookList: List) 9 | 10 | data class BookInfo( 11 | val id: Long, 12 | val title: String, 13 | val author: String, 14 | val isRental: Boolean 15 | ) { 16 | constructor(model: BookWithRental) : this(model.book.id, model.book.title, model.book.author, model.isRental) 17 | } 18 | 19 | data class GetBookDetailResponse( 20 | val id: Long, 21 | val title: String, 22 | val author: String, 23 | val releaseDate: LocalDate, 24 | val rentalInfo: RentalInfo? 25 | ) { 26 | constructor(model: BookWithRental) : this( 27 | model.book.id, 28 | model.book.title, 29 | model.book.author, 30 | model.book.releaseDate, 31 | model.rental?.let { RentalInfo(model.rental) }) 32 | } 33 | 34 | data class RentalInfo( 35 | val userId: Long, 36 | val rentalDatetime: LocalDateTime, 37 | val returnDeadline: LocalDateTime, 38 | ) { 39 | constructor(rental: Rental) : this(rental.userId, rental.rentalDatetime, rental.returnDeadline) 40 | } 41 | 42 | data class RegisterBookRequest( 43 | val id: Long, 44 | val title: String, 45 | val author: String, 46 | val releaseDate: LocalDate 47 | ) 48 | 49 | data class UpdateBookRequest( 50 | val id: Long, 51 | val title: String?, 52 | val author: String?, 53 | val releaseDate: LocalDate? 54 | ) -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/presentation/form/RentalForm.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.presentation.form 2 | 3 | data class RentalStartRequest( 4 | val bookId: Long 5 | ) 6 | -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/presentation/handler/BookManagerAccessDeniedHandler.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.presentation.handler 2 | 3 | import org.springframework.security.access.AccessDeniedException 4 | import org.springframework.security.web.access.AccessDeniedHandler 5 | import javax.servlet.http.HttpServletRequest 6 | import javax.servlet.http.HttpServletResponse 7 | 8 | class BookManagerAccessDeniedHandler : AccessDeniedHandler { 9 | override fun handle( 10 | request: HttpServletRequest, 11 | response: HttpServletResponse, 12 | accessDeniedException: AccessDeniedException 13 | ) { 14 | response.status = HttpServletResponse.SC_FORBIDDEN 15 | } 16 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/presentation/handler/BookManagerAuthenticationEntryPoint.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.presentation.handler 2 | 3 | import org.springframework.security.core.AuthenticationException 4 | import org.springframework.security.web.AuthenticationEntryPoint 5 | import javax.servlet.http.HttpServletRequest 6 | import javax.servlet.http.HttpServletResponse 7 | 8 | class BookManagerAuthenticationEntryPoint : AuthenticationEntryPoint { 9 | override fun commence( 10 | request: HttpServletRequest, 11 | response: HttpServletResponse, 12 | authException: AuthenticationException 13 | ) { 14 | response.status = HttpServletResponse.SC_UNAUTHORIZED 15 | } 16 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/presentation/handler/BookManagerAuthenticationFailureHandler.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.presentation.handler 2 | 3 | import org.springframework.security.core.AuthenticationException 4 | import org.springframework.security.web.authentication.AuthenticationFailureHandler 5 | import javax.servlet.http.HttpServletRequest 6 | import javax.servlet.http.HttpServletResponse 7 | 8 | class BookManagerAuthenticationFailureHandler : AuthenticationFailureHandler { 9 | override fun onAuthenticationFailure( 10 | request: HttpServletRequest, 11 | response: HttpServletResponse, 12 | exception: AuthenticationException 13 | ) { 14 | response.status = HttpServletResponse.SC_UNAUTHORIZED 15 | } 16 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/kotlin/com/book/manager/presentation/handler/BookManagerAuthenticationSuccessHandler.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.presentation.handler 2 | 3 | import org.springframework.security.core.Authentication 4 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler 5 | import javax.servlet.http.HttpServletRequest 6 | import javax.servlet.http.HttpServletResponse 7 | 8 | class BookManagerAuthenticationSuccessHandler : AuthenticationSuccessHandler { 9 | override fun onAuthenticationSuccess( 10 | request: HttpServletRequest, 11 | response: HttpServletResponse, 12 | authentication: Authentication 13 | ) { 14 | response.status = HttpServletResponse.SC_OK 15 | } 16 | } -------------------------------------------------------------------------------- /part2/book-manager/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | datasource: 3 | url: jdbc:mysql://127.0.0.1:3306/book_manager?characterEncoding=utf8 4 | username: root 5 | password: mysql 6 | driverClassName: com.mysql.jdbc.Driver 7 | jackson: 8 | property-naming-strategy: SNAKE_CASE 9 | # ロギングに関する設定 10 | logging: 11 | level: 12 | root: INFO -------------------------------------------------------------------------------- /part2/book-manager/src/main/resources/generatorConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 31 | 32 | 33 | 35 |
36 |
37 |
38 | -------------------------------------------------------------------------------- /part2/book-manager/src/test/kotlin/com/book/manager/BookManagerApplicationTests.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager 2 | 3 | import org.junit.jupiter.api.Test 4 | import org.springframework.boot.test.context.SpringBootTest 5 | 6 | @SpringBootTest 7 | class BookManagerApplicationTests { 8 | 9 | @Test 10 | fun contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part2/book-manager/src/test/kotlin/com/book/manager/application/service/BookServiceTest.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.application.service 2 | 3 | import com.book.manager.domain.model.Book 4 | import com.book.manager.domain.model.BookWithRental 5 | import com.book.manager.domain.repository.BookRepository 6 | import com.nhaarman.mockitokotlin2.mock 7 | import com.nhaarman.mockitokotlin2.whenever 8 | import org.assertj.core.api.Assertions 9 | import org.junit.jupiter.api.Test 10 | import java.time.LocalDate 11 | 12 | internal class BookServiceTest { 13 | private val bookRepository = mock() 14 | 15 | private val bookService = BookService(bookRepository) 16 | 17 | @Test 18 | fun `getList when book list is exist then return list`() { 19 | val book = Book(1, "Kotlin入門", "コトリン太郎", LocalDate.now()) 20 | val bookWithRental = BookWithRental(book, null) 21 | val expected = listOf(bookWithRental) 22 | 23 | whenever(bookRepository.findAllWithRental()).thenReturn(expected) 24 | 25 | val result = bookService.getList() 26 | Assertions.assertThat(expected).isEqualTo(result) 27 | } 28 | } -------------------------------------------------------------------------------- /part2/book-manager/src/test/kotlin/com/book/manager/application/service/RentalServiceTest.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.application.service 2 | 3 | import com.book.manager.domain.enum.RoleType 4 | import com.book.manager.domain.model.Book 5 | import com.book.manager.domain.model.BookWithRental 6 | import com.book.manager.domain.model.Rental 7 | import com.book.manager.domain.model.User 8 | import com.book.manager.domain.repository.BookRepository 9 | import com.book.manager.domain.repository.RentalRepository 10 | import com.book.manager.domain.repository.UserRepository 11 | import com.nhaarman.mockitokotlin2.any 12 | import com.nhaarman.mockitokotlin2.mock 13 | import com.nhaarman.mockitokotlin2.times 14 | import com.nhaarman.mockitokotlin2.verify 15 | import com.nhaarman.mockitokotlin2.whenever 16 | import org.assertj.core.api.Assertions.assertThat 17 | import org.junit.jupiter.api.Assertions 18 | import org.junit.jupiter.api.Test 19 | import java.time.LocalDate 20 | import java.time.LocalDateTime 21 | 22 | internal class RentalServiceTest { 23 | private val userRepository = mock() 24 | private val bookRepository = mock() 25 | private val rentalRepository = mock() 26 | 27 | private val rentalService = RentalService(userRepository, bookRepository, rentalRepository) 28 | 29 | @Test 30 | fun `endRental when book is rental then delete to rental`() { 31 | val userId = 100L 32 | val bookId = 1000L 33 | val user = User(userId, "test@test.com", "pass", "kotlin", RoleType.USER) 34 | val book = Book(bookId, "Kotlin入門", "コトリン太郎", LocalDate.now()) 35 | val rental = Rental(bookId, userId, LocalDateTime.now(), LocalDateTime.MAX) 36 | val bookWithRental = BookWithRental(book, rental) 37 | 38 | whenever(userRepository.find(any() as Long)).thenReturn(user) 39 | whenever(bookRepository.findWithRental(any())).thenReturn(bookWithRental) 40 | 41 | rentalService.endRental(bookId, userId) 42 | 43 | verify(userRepository).find(userId) 44 | verify(bookRepository).findWithRental(bookId) 45 | verify(rentalRepository).endRental(bookId) 46 | } 47 | 48 | @Test 49 | fun `endRental when book is not rental then throw exception`() { 50 | val userId = 100L 51 | val bookId = 1000L 52 | val user = User(userId, "test@test.com", "pass", "kotlin", RoleType.USER) 53 | val book = Book(bookId, "Kotlin入門", "コトリン太郎", LocalDate.now()) 54 | val bookWithRental = BookWithRental(book, null) 55 | 56 | whenever(userRepository.find(any() as Long)).thenReturn(user) 57 | whenever(bookRepository.findWithRental(any())).thenReturn(bookWithRental) 58 | 59 | val exception = Assertions.assertThrows(IllegalStateException::class.java) { 60 | rentalService.endRental(bookId, userId) 61 | } 62 | 63 | assertThat(exception.message).isEqualTo("未貸出の商品です bookId:${bookId}") 64 | 65 | verify(userRepository).find(userId) 66 | verify(bookRepository).findWithRental(bookId) 67 | verify(rentalRepository, times(0)).endRental(any()) 68 | } 69 | } -------------------------------------------------------------------------------- /part2/book-manager/src/test/kotlin/com/book/manager/domain/model/BookWithRentalTest.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.domain.model 2 | 3 | import org.assertj.core.api.Assertions 4 | import org.junit.jupiter.api.Test 5 | import java.time.LocalDate 6 | import java.time.LocalDateTime 7 | 8 | internal class BookWithRentalTest { 9 | @Test 10 | fun `isRental when rental is null then return false`() { 11 | val book = Book(1, "Kotlin入門", "コトリン太郎", LocalDate.now()) 12 | val bookWithRental = BookWithRental(book, null) 13 | Assertions.assertThat(bookWithRental.isRental).isEqualTo(false) 14 | } 15 | 16 | @Test 17 | fun `isRental when rental is not null then return true`() { 18 | val book = Book(1, "Kotlin入門", "コトリン太郎", LocalDate.now()) 19 | val rental = Rental(1, 100, LocalDateTime.now(), LocalDateTime.MAX) 20 | val bookWithRental = BookWithRental(book, rental) 21 | Assertions.assertThat(bookWithRental.isRental).isEqualTo(true) 22 | } 23 | } -------------------------------------------------------------------------------- /part2/book-manager/src/test/kotlin/com/book/manager/presentation/controller/BookControllerTest.kt: -------------------------------------------------------------------------------- 1 | package com.book.manager.presentation.controller 2 | 3 | import com.book.manager.application.service.BookService 4 | import com.book.manager.domain.model.Book 5 | import com.book.manager.domain.model.BookWithRental 6 | import com.book.manager.presentation.form.BookInfo 7 | import com.book.manager.presentation.form.GetBookListResponse 8 | import com.fasterxml.jackson.databind.ObjectMapper 9 | import com.fasterxml.jackson.module.kotlin.registerKotlinModule 10 | import com.nhaarman.mockitokotlin2.mock 11 | import com.nhaarman.mockitokotlin2.whenever 12 | import org.assertj.core.api.Assertions 13 | import org.junit.jupiter.api.Test 14 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get 15 | import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status 16 | import org.springframework.test.web.servlet.setup.MockMvcBuilders 17 | import java.nio.charset.StandardCharsets 18 | import java.time.LocalDate 19 | 20 | internal class BookControllerTest { 21 | private val bookService = mock() 22 | private val bookController = BookController(bookService) 23 | 24 | @Test 25 | fun `getList is success`() { 26 | val bookId = 100L 27 | val book = Book(bookId, "Kotlin入門", "コトリン太郎", LocalDate.now()) 28 | val bookList = listOf(BookWithRental(book, null)) 29 | 30 | whenever(bookService.getList()).thenReturn(bookList) 31 | 32 | val expectedResponse = GetBookListResponse(listOf(BookInfo(bookId, "Kotlin入門", "コトリン太郎", false))) 33 | val expected = ObjectMapper().registerKotlinModule().writeValueAsString(expectedResponse) 34 | val mockMvc = MockMvcBuilders.standaloneSetup(bookController).build() 35 | val resultResponse = mockMvc.perform(get("/book/list")).andExpect(status().isOk).andReturn().response 36 | val result = resultResponse.getContentAsString(StandardCharsets.UTF_8) 37 | 38 | Assertions.assertThat(expected).isEqualTo(result) 39 | } 40 | } -------------------------------------------------------------------------------- /part2/book-manager/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline -------------------------------------------------------------------------------- /part2/chapter04/README.md: -------------------------------------------------------------------------------- 1 | # 概要 2 | demoディレクトリが、Spring BootアプリケーションのGradleプロジェクトとなっています。 3 | IntelliJ IDEAで [File]->[Open] を選択し、このディレクトリのbuild.gradle.ktsを開いてください。 4 | ダイアログが表示されるので、[Open as Project] を選択してください。 5 | 6 | 4章の内容で作成したクラスごとにファイルがあり、それぞれ追加されていった関数、処理が全て入った状態のものになっています。 7 | 8 | # 実行方法 9 | 書籍内で解説しているものと同様、以下の方法で実行できます。 10 | 11 | - IntelliJ IDEAのGradleビューから、[Tasks]->[application]->[bootRun]を実行 12 | - ターミナルでdemoディレクトリから ```./gradlew bootRun``` を実行 13 | - DemoApplication.ktのmain関数を実行 14 | 15 | # 補足 16 | 複数のリストで同じクラス名や関数名を使っている場合など、並べて書かれているとコンパイルエラーになってしまうものは、コメントアウトされています。 17 | 必要に応じてコメントを外し、実行してください。 18 | コンパイルエラーになる例を紹介しているコードに関しても、同様にコメントアウトされています。 -------------------------------------------------------------------------------- /part2/chapter04/demo/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /part2/chapter04/demo/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | plugins { 4 | id("org.springframework.boot") version "2.4.4" 5 | id("io.spring.dependency-management") version "1.0.11.RELEASE" 6 | kotlin("jvm") version "1.4.31" 7 | kotlin("plugin.spring") version "1.4.31" 8 | } 9 | 10 | group = "com.example" 11 | version = "0.0.1-SNAPSHOT" 12 | java.sourceCompatibility = JavaVersion.VERSION_11 13 | 14 | repositories { 15 | mavenCentral() 16 | } 17 | 18 | dependencies { 19 | implementation("org.springframework.boot:spring-boot-starter-thymeleaf") 20 | implementation("org.springframework.boot:spring-boot-starter-web") 21 | implementation("com.fasterxml.jackson.module:jackson-module-kotlin") 22 | implementation("org.jetbrains.kotlin:kotlin-reflect") 23 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") 24 | testImplementation("org.springframework.boot:spring-boot-starter-test") 25 | } 26 | 27 | tasks.withType { 28 | kotlinOptions { 29 | freeCompilerArgs = listOf("-Xjsr305=strict") 30 | jvmTarget = "11" 31 | } 32 | } 33 | 34 | tasks.withType { 35 | useJUnitPlatform() 36 | } 37 | -------------------------------------------------------------------------------- /part2/chapter04/demo/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-takehata/kotlin-server-side-programming-practice/3526abaf27718941f5d36bd2588ec126e9386ae5/part2/chapter04/demo/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part2/chapter04/demo/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part2/chapter04/demo/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /part2/chapter04/demo/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "demo" 2 | -------------------------------------------------------------------------------- /part2/chapter04/demo/src/main/kotlin/com/example/demo/DemoApplication.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication 4 | import org.springframework.boot.runApplication 5 | 6 | @SpringBootApplication 7 | class DemoApplication 8 | 9 | fun main(args: Array) { 10 | runApplication(*args) 11 | } 12 | -------------------------------------------------------------------------------- /part2/chapter04/demo/src/main/kotlin/com/example/demo/Greeter.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | interface Greeter { 4 | fun sayHello(name: String): String 5 | } -------------------------------------------------------------------------------- /part2/chapter04/demo/src/main/kotlin/com/example/demo/GreeterController.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | import org.springframework.web.bind.annotation.GetMapping 4 | import org.springframework.web.bind.annotation.PathVariable 5 | import org.springframework.web.bind.annotation.PostMapping 6 | import org.springframework.web.bind.annotation.RequestBody 7 | import org.springframework.web.bind.annotation.RequestMapping 8 | import org.springframework.web.bind.annotation.RequestParam 9 | import org.springframework.web.bind.annotation.RestController 10 | 11 | @RestController 12 | @RequestMapping("greeter") 13 | class GreeterController( 14 | private val greeter: Greeter 15 | ) { 16 | @GetMapping("/hello") 17 | fun hello(@RequestParam("name") name: String): HelloResponse { 18 | return HelloResponse("Hello ${name}") 19 | } 20 | 21 | @GetMapping("/hello/{name}") 22 | fun helloPathValue(@PathVariable("name") name: String): HelloResponse { 23 | return HelloResponse("Hello $name") 24 | } 25 | 26 | @PostMapping("/hello") 27 | fun helloByPost(@RequestBody request: HelloRequest): HelloResponse { 28 | return HelloResponse("Hello ${request.name}") 29 | } 30 | 31 | @GetMapping("/hello/byservice/{name}") 32 | fun helloByService(@PathVariable("name") name: String): HelloResponse { 33 | val message = greeter.sayHello(name) 34 | return HelloResponse(message) 35 | } 36 | } -------------------------------------------------------------------------------- /part2/chapter04/demo/src/main/kotlin/com/example/demo/GreeterImpl.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | import org.springframework.stereotype.Component 4 | 5 | @Component 6 | class GreeterImpl : Greeter { 7 | override fun sayHello(name: String) = "Hello $name" 8 | } -------------------------------------------------------------------------------- /part2/chapter04/demo/src/main/kotlin/com/example/demo/HelloController.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | import org.springframework.stereotype.Controller 4 | import org.springframework.ui.Model 5 | import org.springframework.web.bind.annotation.GetMapping 6 | 7 | @Controller 8 | class HelloController { 9 | @GetMapping("/") 10 | fun index(model: Model): String { 11 | model.addAttribute("message", "Hello World!") 12 | return "index" 13 | } 14 | } -------------------------------------------------------------------------------- /part2/chapter04/demo/src/main/kotlin/com/example/demo/HelloRequest.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | data class HelloRequest(val name: String) -------------------------------------------------------------------------------- /part2/chapter04/demo/src/main/kotlin/com/example/demo/HelloResponse.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | data class HelloResponse(val message: String) -------------------------------------------------------------------------------- /part2/chapter04/demo/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /part2/chapter04/demo/src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello World! 6 | 7 | 8 |

9 | 10 | -------------------------------------------------------------------------------- /part2/chapter04/demo/src/test/kotlin/com/example/demo/DemoApplicationTests.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | import org.junit.jupiter.api.Test 4 | import org.springframework.boot.test.context.SpringBootTest 5 | 6 | @SpringBootTest 7 | class DemoApplicationTests { 8 | 9 | @Test 10 | fun contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part2/chapter05/README.md: -------------------------------------------------------------------------------- 1 | # 概要 2 | 「4 MyBatisでCRUDを作成する」までの内容がmybatis-onlyディレクトリ、「5 Spring BootからMyBatisを使用する」の内容がmybatis-with-springbootディレクトリに入っています。 3 | 4 | mybatis-onlyはこのディレクトリ自体がGradleプロジェクトとなっています。 5 | databaseパッケージ配下にMybatis Generatorでの生成コードがあり、それ以外のサンプルコードは全てMain.ktに入っています。 6 | 各コード上の書籍との対応は、以下のようにリスト番号をコメントで記載しています。 7 | 8 | ``` 9 | /** 10 | * リスト5.4.2 11 | */ 12 | ``` 13 | 14 | また、src/main/resources配下に各種設定ファイルのサンプルコードも入っています。 15 | 16 | mybatis-with-springbootはその下にdemoディレクトリがあり、こちらがSpring BootアプリケーションのGradleプロジェクトとなっています。 17 | 内容は4章のdemoプロジェクトに、5章の「5 Spring BootからMyBatisを使用する」で作成した処理を加えたものになっています。 18 | 19 | いずれもIntelliJ IDEAで [File]->[Open] を選択し、このディレクトリのbuild.gradle.ktsを開いてください。 20 | ダイアログが表示されるので、[Open as Project] を選択してください。 21 | 22 | # 実行方法 23 | ## mybatis-only 24 | クラスの定義などを除く、実行結果の伴うサンプルコードは、リストごとに関数として処理を記述しています。 25 | 一番上にあるmain関数を以下のように書き換え、実行したいリストの関数を呼び出す形に変更することで、動作確認をできます。 26 | 27 | ``` 28 | fun main() { 29 | list5_4_4() 30 | } 31 | ``` 32 | 33 | ## mybatis-with-springboot/demo 34 | 以下の方法で実行できます。 35 | 36 | - IntelliJ IDEAのGradleビューから、[Tasks]->[application]->[bootRun]を実行 37 | - ターミナルでdemoディレクトリから ```./gradlew bootRun``` を実行 38 | - DemoApplication.ktのmain関数を実行 39 | 40 | # 補足 41 | 複数のリストで同じクラス名や関数名を使っている場合など、並べて書かれているとコンパイルエラーになってしまうものは、コメントアウトされています。 42 | 必要に応じてコメントを外し、実行してください。 43 | コンパイルエラーになる例を紹介しているコードに関しても、同様にコメントアウトされています。 44 | -------------------------------------------------------------------------------- /part2/chapter05/mybatis-only/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | plugins { 4 | id("com.arenagod.gradle.MybatisGenerator") version "1.4" 5 | kotlin("jvm") version "1.4.21" 6 | } 7 | 8 | group = "org.example" 9 | version = "1.0-SNAPSHOT" 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | dependencies { 16 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") 17 | implementation("org.mybatis:mybatis:3.5.6") 18 | implementation("org.mybatis.dynamic-sql:mybatis-dynamic-sql:1.2.1") 19 | implementation("mysql:mysql-connector-java:8.0.23") 20 | mybatisGenerator("org.mybatis.generator:mybatis-generator-core:1.4.0") 21 | testImplementation(kotlin("test-junit")) 22 | } 23 | 24 | tasks.withType() { 25 | kotlinOptions.jvmTarget = "11" 26 | } 27 | 28 | mybatisGenerator { 29 | verbose = true 30 | configFile = "${projectDir}/src/main/resources/generatorConfig.xml" 31 | } -------------------------------------------------------------------------------- /part2/chapter05/mybatis-only/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official -------------------------------------------------------------------------------- /part2/chapter05/mybatis-only/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-takehata/kotlin-server-side-programming-practice/3526abaf27718941f5d36bd2588ec126e9386ae5/part2/chapter05/mybatis-only/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part2/chapter05/mybatis-only/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part2/chapter05/mybatis-only/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' } 4 | 5 | mavenCentral() 6 | 7 | maven { url 'https://plugins.gradle.org/m2/' } 8 | } 9 | } 10 | rootProject.name = 'mybatis-only' 11 | 12 | -------------------------------------------------------------------------------- /part2/chapter05/mybatis-only/src/main/kotlin/database/UserDynamicSqlSupport.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Auto-generated file. Created by MyBatis Generator 3 | */ 4 | package database 5 | 6 | import java.sql.JDBCType 7 | import org.mybatis.dynamic.sql.SqlTable 8 | 9 | object UserDynamicSqlSupport { 10 | object User : SqlTable("user") { 11 | val id = column("id", JDBCType.INTEGER) 12 | 13 | val name = column("name", JDBCType.VARCHAR) 14 | 15 | val age = column("age", JDBCType.INTEGER) 16 | 17 | val profile = column("profile", JDBCType.VARCHAR) 18 | } 19 | } -------------------------------------------------------------------------------- /part2/chapter05/mybatis-only/src/main/kotlin/database/UserMapper.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Auto-generated file. Created by MyBatis Generator 3 | */ 4 | package database 5 | 6 | import database.UserRecord 7 | import org.apache.ibatis.annotations.DeleteProvider 8 | import org.apache.ibatis.annotations.InsertProvider 9 | import org.apache.ibatis.annotations.Mapper 10 | import org.apache.ibatis.annotations.Result 11 | import org.apache.ibatis.annotations.ResultMap 12 | import org.apache.ibatis.annotations.Results 13 | import org.apache.ibatis.annotations.SelectProvider 14 | import org.apache.ibatis.annotations.UpdateProvider 15 | import org.apache.ibatis.type.JdbcType 16 | import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider 17 | import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider 18 | import org.mybatis.dynamic.sql.insert.render.MultiRowInsertStatementProvider 19 | import org.mybatis.dynamic.sql.select.render.SelectStatementProvider 20 | import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider 21 | import org.mybatis.dynamic.sql.util.SqlProviderAdapter 22 | 23 | @Mapper 24 | interface UserMapper { 25 | @SelectProvider(type=SqlProviderAdapter::class, method="select") 26 | fun count(selectStatement: SelectStatementProvider): Long 27 | 28 | @DeleteProvider(type=SqlProviderAdapter::class, method="delete") 29 | fun delete(deleteStatement: DeleteStatementProvider): Int 30 | 31 | @InsertProvider(type=SqlProviderAdapter::class, method="insert") 32 | fun insert(insertStatement: InsertStatementProvider): Int 33 | 34 | @InsertProvider(type=SqlProviderAdapter::class, method="insertMultiple") 35 | fun insertMultiple(multipleInsertStatement: MultiRowInsertStatementProvider): Int 36 | 37 | @SelectProvider(type=SqlProviderAdapter::class, method="select") 38 | @ResultMap("UserRecordResult") 39 | fun selectOne(selectStatement: SelectStatementProvider): UserRecord? 40 | 41 | @SelectProvider(type=SqlProviderAdapter::class, method="select") 42 | @Results(id="UserRecordResult", value = [ 43 | Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true), 44 | Result(column="name", property="name", jdbcType=JdbcType.VARCHAR), 45 | Result(column="age", property="age", jdbcType=JdbcType.INTEGER), 46 | Result(column="profile", property="profile", jdbcType=JdbcType.VARCHAR) 47 | ]) 48 | fun selectMany(selectStatement: SelectStatementProvider): List 49 | 50 | @UpdateProvider(type=SqlProviderAdapter::class, method="update") 51 | fun update(updateStatement: UpdateStatementProvider): Int 52 | } -------------------------------------------------------------------------------- /part2/chapter05/mybatis-only/src/main/kotlin/database/UserRecord.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Auto-generated file. Created by MyBatis Generator 3 | */ 4 | package database 5 | 6 | data class UserRecord( 7 | var id: Int? = null, 8 | var name: String? = null, 9 | var age: Int? = null, 10 | var profile: String? = null 11 | ) -------------------------------------------------------------------------------- /part2/chapter05/mybatis-only/src/main/resources/generatorConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 24 | 25 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /part2/chapter05/mybatis-only/src/main/resources/mybatis-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /part2/chapter05/mybatis-only/src/main/resources/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "resources", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | plugins { 4 | id("org.springframework.boot") version "2.4.4" 5 | id("io.spring.dependency-management") version "1.0.11.RELEASE" 6 | id("com.arenagod.gradle.MybatisGenerator") version "1.4" 7 | kotlin("jvm") version "1.4.31" 8 | kotlin("plugin.spring") version "1.4.31" 9 | } 10 | 11 | group = "com.example" 12 | version = "0.0.1-SNAPSHOT" 13 | java.sourceCompatibility = JavaVersion.VERSION_11 14 | 15 | repositories { 16 | mavenCentral() 17 | } 18 | 19 | dependencies { 20 | implementation("org.springframework.boot:spring-boot-starter-thymeleaf") 21 | implementation("org.springframework.boot:spring-boot-starter-web") 22 | implementation("com.fasterxml.jackson.module:jackson-module-kotlin") 23 | implementation("org.jetbrains.kotlin:kotlin-reflect") 24 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") 25 | implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.4") 26 | implementation("org.mybatis.dynamic-sql:mybatis-dynamic-sql:1.2.1") 27 | implementation("mysql:mysql-connector-java:8.0.23") 28 | mybatisGenerator("org.mybatis.generator:mybatis-generator-core:1.4.0") 29 | testImplementation("org.springframework.boot:spring-boot-starter-test") 30 | } 31 | 32 | tasks.withType { 33 | kotlinOptions { 34 | freeCompilerArgs = listOf("-Xjsr305=strict") 35 | jvmTarget = "11" 36 | } 37 | } 38 | 39 | tasks.withType { 40 | useJUnitPlatform() 41 | } 42 | 43 | mybatisGenerator { 44 | verbose = true 45 | configFile = "${projectDir}/src/main/resources/generatorConfig.xml" 46 | } -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-takehata/kotlin-server-side-programming-practice/3526abaf27718941f5d36bd2588ec126e9386ae5/part2/chapter05/mybatis-with-springboot/demo/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "demo" 2 | -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/src/main/kotlin/com/example/demo/DemoApplication.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication 4 | import org.springframework.boot.runApplication 5 | 6 | @SpringBootApplication 7 | class DemoApplication 8 | 9 | fun main(args: Array) { 10 | runApplication(*args) 11 | } 12 | -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/src/main/kotlin/com/example/demo/Greeter.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | interface Greeter { 4 | fun sayHello(name: String): String 5 | } -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/src/main/kotlin/com/example/demo/GreeterController.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | import org.springframework.web.bind.annotation.GetMapping 4 | import org.springframework.web.bind.annotation.PathVariable 5 | import org.springframework.web.bind.annotation.PostMapping 6 | import org.springframework.web.bind.annotation.RequestBody 7 | import org.springframework.web.bind.annotation.RequestMapping 8 | import org.springframework.web.bind.annotation.RequestParam 9 | import org.springframework.web.bind.annotation.RestController 10 | 11 | @RestController 12 | @RequestMapping("greeter") 13 | class GreeterController( 14 | private val greeter: Greeter 15 | ) { 16 | @GetMapping("/hello") 17 | fun hello(@RequestParam("name") name: String): HelloResponse { 18 | return HelloResponse("Hello ${name}") 19 | } 20 | 21 | @GetMapping("/hello/{name}") 22 | fun helloPathValue(@PathVariable("name") name: String): HelloResponse { 23 | return HelloResponse("Hello $name") 24 | } 25 | 26 | @PostMapping("/hello") 27 | fun helloByPost(@RequestBody request: HelloRequest): HelloResponse { 28 | return HelloResponse("Hello ${request.name}") 29 | } 30 | 31 | @GetMapping("/hello/byservice/{name}") 32 | fun helloByService(@PathVariable("name") name: String): HelloResponse { 33 | val message = greeter.sayHello(name) 34 | return HelloResponse(message) 35 | } 36 | } -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/src/main/kotlin/com/example/demo/GreeterImpl.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | import org.springframework.stereotype.Component 4 | 5 | @Component 6 | class GreeterImpl : Greeter { 7 | override fun sayHello(name: String) = "Hello $name" 8 | } -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/src/main/kotlin/com/example/demo/HelloController.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | import org.springframework.stereotype.Controller 4 | import org.springframework.ui.Model 5 | import org.springframework.web.bind.annotation.GetMapping 6 | 7 | @Controller 8 | class HelloController { 9 | @GetMapping("/") 10 | fun index(model: Model): String { 11 | model.addAttribute("message", "Hello World!") 12 | return "index" 13 | } 14 | } -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/src/main/kotlin/com/example/demo/HelloRequest.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | data class HelloRequest(val name: String) -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/src/main/kotlin/com/example/demo/HelloResponse.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | data class HelloResponse(val message: String) -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/src/main/kotlin/com/example/demo/UserController.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | import com.example.demo.database.UserMapper 4 | import com.example.demo.database.UserRecord 5 | import com.example.demo.database.insert 6 | import com.example.demo.database.selectByPrimaryKey 7 | import org.springframework.web.bind.annotation.GetMapping 8 | import org.springframework.web.bind.annotation.PathVariable 9 | import org.springframework.web.bind.annotation.PostMapping 10 | import org.springframework.web.bind.annotation.RequestBody 11 | import org.springframework.web.bind.annotation.RequestMapping 12 | import org.springframework.web.bind.annotation.RestController 13 | 14 | @Suppress("SpringJavaInjectionPointsAutowiringInspection") 15 | @RestController 16 | @RequestMapping("user") 17 | class UserController( 18 | val userMapper: UserMapper 19 | ) { 20 | @GetMapping("/select/{id}") 21 | fun select(@PathVariable("id") id: Int): UserRecord? { 22 | return userMapper.selectByPrimaryKey(id) 23 | } 24 | 25 | @PostMapping("/insert") 26 | fun insert(@RequestBody request: InsertRequest): InsertResponse { 27 | val record = UserRecord(request.id, request.name, request.age, request.profile) 28 | return InsertResponse(userMapper.insert(record)) 29 | } 30 | } 31 | 32 | // リクエスト 33 | data class InsertRequest(val id: Int, val name: String, val age: Int, val profile: String) 34 | 35 | // レスポンス 36 | data class InsertResponse(val count: Int) -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/src/main/kotlin/com/example/demo/database/UserDynamicSqlSupport.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Auto-generated file. Created by MyBatis Generator 3 | */ 4 | package com.example.demo.database 5 | 6 | import java.sql.JDBCType 7 | import org.mybatis.dynamic.sql.SqlTable 8 | 9 | object UserDynamicSqlSupport { 10 | object User : SqlTable("user") { 11 | val id = column("id", JDBCType.INTEGER) 12 | 13 | val name = column("name", JDBCType.VARCHAR) 14 | 15 | val age = column("age", JDBCType.INTEGER) 16 | 17 | val profile = column("profile", JDBCType.VARCHAR) 18 | } 19 | } -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/src/main/kotlin/com/example/demo/database/UserMapper.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Auto-generated file. Created by MyBatis Generator 3 | */ 4 | package com.example.demo.database 5 | 6 | import com.example.demo.database.UserRecord 7 | import org.apache.ibatis.annotations.DeleteProvider 8 | import org.apache.ibatis.annotations.InsertProvider 9 | import org.apache.ibatis.annotations.Mapper 10 | import org.apache.ibatis.annotations.Result 11 | import org.apache.ibatis.annotations.ResultMap 12 | import org.apache.ibatis.annotations.Results 13 | import org.apache.ibatis.annotations.SelectProvider 14 | import org.apache.ibatis.annotations.UpdateProvider 15 | import org.apache.ibatis.type.JdbcType 16 | import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider 17 | import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider 18 | import org.mybatis.dynamic.sql.insert.render.MultiRowInsertStatementProvider 19 | import org.mybatis.dynamic.sql.select.render.SelectStatementProvider 20 | import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider 21 | import org.mybatis.dynamic.sql.util.SqlProviderAdapter 22 | 23 | @Mapper 24 | interface UserMapper { 25 | @SelectProvider(type=SqlProviderAdapter::class, method="select") 26 | fun count(selectStatement: SelectStatementProvider): Long 27 | 28 | @DeleteProvider(type=SqlProviderAdapter::class, method="delete") 29 | fun delete(deleteStatement: DeleteStatementProvider): Int 30 | 31 | @InsertProvider(type=SqlProviderAdapter::class, method="insert") 32 | fun insert(insertStatement: InsertStatementProvider): Int 33 | 34 | @InsertProvider(type=SqlProviderAdapter::class, method="insertMultiple") 35 | fun insertMultiple(multipleInsertStatement: MultiRowInsertStatementProvider): Int 36 | 37 | @SelectProvider(type=SqlProviderAdapter::class, method="select") 38 | @ResultMap("UserRecordResult") 39 | fun selectOne(selectStatement: SelectStatementProvider): UserRecord? 40 | 41 | @SelectProvider(type=SqlProviderAdapter::class, method="select") 42 | @Results(id="UserRecordResult", value = [ 43 | Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true), 44 | Result(column="name", property="name", jdbcType=JdbcType.VARCHAR), 45 | Result(column="age", property="age", jdbcType=JdbcType.INTEGER), 46 | Result(column="profile", property="profile", jdbcType=JdbcType.VARCHAR) 47 | ]) 48 | fun selectMany(selectStatement: SelectStatementProvider): List 49 | 50 | @UpdateProvider(type=SqlProviderAdapter::class, method="update") 51 | fun update(updateStatement: UpdateStatementProvider): Int 52 | } -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/src/main/kotlin/com/example/demo/database/UserRecord.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Auto-generated file. Created by MyBatis Generator 3 | */ 4 | package com.example.demo.database 5 | 6 | data class UserRecord( 7 | var id: Int? = null, 8 | var name: String? = null, 9 | var age: Int? = null, 10 | var profile: String? = null 11 | ) -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | datasource: 3 | url: jdbc:mysql://127.0.0.1:3306/example?characterEncoding=utf8 4 | username: root 5 | password: mysql 6 | driverClassName: com.mysql.jdbc.Driver -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/src/main/resources/generatorConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 24 | 25 | 28 | 29 |
30 | 31 | 32 | -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello World! 6 | 7 | 8 |

9 | 10 | -------------------------------------------------------------------------------- /part2/chapter05/mybatis-with-springboot/demo/src/test/kotlin/com/example/demo/DemoApplicationTests.kt: -------------------------------------------------------------------------------- 1 | package com.example.demo 2 | 3 | import org.junit.jupiter.api.Test 4 | import org.springframework.boot.test.context.SpringBootTest 5 | 6 | @SpringBootTest 7 | class DemoApplicationTests { 8 | 9 | @Test 10 | fun contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part2/front/book-manager/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-vue-jsx", "transform-runtime"], 12 | "env": { 13 | "test": { 14 | "presets": ["env", "stage-2"], 15 | "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /part2/front/book-manager/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /part2/front/book-manager/.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /config/ 3 | /dist/ 4 | /*.js 5 | /test/unit/coverage/ 6 | -------------------------------------------------------------------------------- /part2/front/book-manager/.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parserOptions: { 6 | parser: 'babel-eslint' 7 | }, 8 | env: { 9 | browser: true, 10 | }, 11 | extends: [ 12 | // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention 13 | // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. 14 | 'plugin:vue/essential', 15 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md 16 | 'standard' 17 | ], 18 | // required to lint *.vue files 19 | plugins: [ 20 | 'vue' 21 | ], 22 | // add your custom rules here 23 | rules: { 24 | // allow async-await 25 | 'generator-star-spacing': 'off', 26 | // allow debugger during development 27 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /part2/front/book-manager/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | /test/unit/coverage/ 8 | /test/e2e/reports/ 9 | selenium-debug.log 10 | 11 | # Editor directories and files 12 | .idea 13 | .vscode 14 | *.suo 15 | *.ntvs* 16 | *.njsproj 17 | *.sln 18 | -------------------------------------------------------------------------------- /part2/front/book-manager/.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /part2/front/book-manager/README.md: -------------------------------------------------------------------------------- 1 | # 概要 2 | part2/book-managerのサーバーアプリケーションのAPIを実行し、画面を表示するフロントエンドのプログラムです。 3 | front/book-managerディレクトリが、Vue.jsのアプリケーションとなっています。 4 | 5 | # 実行方法 6 | npmを使用して起動します。ターミナルからfront/book-managerディレクトリで ```install``` を実行後、```run dev``` で起動します。 7 | 8 | ``` bash 9 | npm install 10 | ``` 11 | 12 | ``` bash 13 | npm run dev 14 | ``` 15 | 16 | 以下のようなメッセージが出力されれば起動成功です。 17 | 18 | ```bash 19 | Your application is running here: http://localhost:8081 20 | ``` 21 | 22 | # 補足 23 | あくまで疎通しての動作確認のためのプログラムのため、UIや画面遷移など簡略的な形で作られています。 24 | そのため、ソースコードも「とりあえず動かしてみる」ができる程度の作りになっているので、品質面で低い部分もあるかもしれませんがご了承ください。 -------------------------------------------------------------------------------- /part2/front/book-manager/build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | process.env.NODE_ENV = 'production' 5 | 6 | const ora = require('ora') 7 | const rm = require('rimraf') 8 | const path = require('path') 9 | const chalk = require('chalk') 10 | const webpack = require('webpack') 11 | const config = require('../config') 12 | const webpackConfig = require('./webpack.prod.conf') 13 | 14 | const spinner = ora('building for production...') 15 | spinner.start() 16 | 17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 18 | if (err) throw err 19 | webpack(webpackConfig, (err, stats) => { 20 | spinner.stop() 21 | if (err) throw err 22 | process.stdout.write(stats.toString({ 23 | colors: true, 24 | modules: false, 25 | children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. 26 | chunks: false, 27 | chunkModules: false 28 | }) + '\n\n') 29 | 30 | if (stats.hasErrors()) { 31 | console.log(chalk.red(' Build failed with errors.\n')) 32 | process.exit(1) 33 | } 34 | 35 | console.log(chalk.cyan(' Build complete.\n')) 36 | console.log(chalk.yellow( 37 | ' Tip: built files are meant to be served over an HTTP server.\n' + 38 | ' Opening index.html over file:// won\'t work.\n' 39 | )) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /part2/front/book-manager/build/check-versions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const chalk = require('chalk') 3 | const semver = require('semver') 4 | const packageConfig = require('../package.json') 5 | const shell = require('shelljs') 6 | 7 | function exec (cmd) { 8 | return require('child_process').execSync(cmd).toString().trim() 9 | } 10 | 11 | const versionRequirements = [ 12 | { 13 | name: 'node', 14 | currentVersion: semver.clean(process.version), 15 | versionRequirement: packageConfig.engines.node 16 | } 17 | ] 18 | 19 | if (shell.which('npm')) { 20 | versionRequirements.push({ 21 | name: 'npm', 22 | currentVersion: exec('npm --version'), 23 | versionRequirement: packageConfig.engines.npm 24 | }) 25 | } 26 | 27 | module.exports = function () { 28 | const warnings = [] 29 | 30 | for (let i = 0; i < versionRequirements.length; i++) { 31 | const mod = versionRequirements[i] 32 | 33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 34 | warnings.push(mod.name + ': ' + 35 | chalk.red(mod.currentVersion) + ' should be ' + 36 | chalk.green(mod.versionRequirement) 37 | ) 38 | } 39 | } 40 | 41 | if (warnings.length) { 42 | console.log('') 43 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 44 | console.log() 45 | 46 | for (let i = 0; i < warnings.length; i++) { 47 | const warning = warnings[i] 48 | console.log(' ' + warning) 49 | } 50 | 51 | console.log() 52 | process.exit(1) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /part2/front/book-manager/build/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-takehata/kotlin-server-side-programming-practice/3526abaf27718941f5d36bd2588ec126e9386ae5/part2/front/book-manager/build/logo.png -------------------------------------------------------------------------------- /part2/front/book-manager/build/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const config = require('../config') 4 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 5 | const packageConfig = require('../package.json') 6 | 7 | exports.assetsPath = function (_path) { 8 | const assetsSubDirectory = process.env.NODE_ENV === 'production' 9 | ? config.build.assetsSubDirectory 10 | : config.dev.assetsSubDirectory 11 | 12 | return path.posix.join(assetsSubDirectory, _path) 13 | } 14 | 15 | exports.cssLoaders = function (options) { 16 | options = options || {} 17 | 18 | const cssLoader = { 19 | loader: 'css-loader', 20 | options: { 21 | sourceMap: options.sourceMap 22 | } 23 | } 24 | 25 | const postcssLoader = { 26 | loader: 'postcss-loader', 27 | options: { 28 | sourceMap: options.sourceMap 29 | } 30 | } 31 | 32 | // generate loader string to be used with extract text plugin 33 | function generateLoaders (loader, loaderOptions) { 34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] 35 | 36 | if (loader) { 37 | loaders.push({ 38 | loader: loader + '-loader', 39 | options: Object.assign({}, loaderOptions, { 40 | sourceMap: options.sourceMap 41 | }) 42 | }) 43 | } 44 | 45 | // Extract CSS when that option is specified 46 | // (which is the case during production build) 47 | if (options.extract) { 48 | return ExtractTextPlugin.extract({ 49 | use: loaders, 50 | fallback: 'vue-style-loader' 51 | }) 52 | } else { 53 | return ['vue-style-loader'].concat(loaders) 54 | } 55 | } 56 | 57 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 58 | return { 59 | css: generateLoaders(), 60 | postcss: generateLoaders(), 61 | less: generateLoaders('less'), 62 | sass: generateLoaders('sass', { indentedSyntax: true }), 63 | scss: generateLoaders('sass'), 64 | stylus: generateLoaders('stylus'), 65 | styl: generateLoaders('stylus') 66 | } 67 | } 68 | 69 | // Generate loaders for standalone style files (outside of .vue) 70 | exports.styleLoaders = function (options) { 71 | const output = [] 72 | const loaders = exports.cssLoaders(options) 73 | 74 | for (const extension in loaders) { 75 | const loader = loaders[extension] 76 | output.push({ 77 | test: new RegExp('\\.' + extension + '$'), 78 | use: loader 79 | }) 80 | } 81 | 82 | return output 83 | } 84 | 85 | exports.createNotifierCallback = () => { 86 | const notifier = require('node-notifier') 87 | 88 | return (severity, errors) => { 89 | if (severity !== 'error') return 90 | 91 | const error = errors[0] 92 | const filename = error.file && error.file.split('!').pop() 93 | 94 | notifier.notify({ 95 | title: packageConfig.name, 96 | message: severity + ': ' + error.name, 97 | subtitle: filename || '', 98 | icon: path.join(__dirname, 'logo.png') 99 | }) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /part2/front/book-manager/build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const config = require('../config') 4 | const isProduction = process.env.NODE_ENV === 'production' 5 | const sourceMapEnabled = isProduction 6 | ? config.build.productionSourceMap 7 | : config.dev.cssSourceMap 8 | 9 | module.exports = { 10 | loaders: utils.cssLoaders({ 11 | sourceMap: sourceMapEnabled, 12 | extract: isProduction 13 | }), 14 | cssSourceMap: sourceMapEnabled, 15 | cacheBusting: config.dev.cacheBusting, 16 | transformToRequire: { 17 | video: ['src', 'poster'], 18 | source: 'src', 19 | img: 'src', 20 | image: 'xlink:href' 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /part2/front/book-manager/build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const config = require('../config') 5 | const vueLoaderConfig = require('./vue-loader.conf') 6 | 7 | function resolve (dir) { 8 | return path.join(__dirname, '..', dir) 9 | } 10 | 11 | const createLintingRule = () => ({ 12 | test: /\.(js|vue)$/, 13 | loader: 'eslint-loader', 14 | enforce: 'pre', 15 | include: [resolve('src'), resolve('test')], 16 | options: { 17 | formatter: require('eslint-friendly-formatter'), 18 | emitWarning: !config.dev.showEslintErrorsInOverlay 19 | } 20 | }) 21 | 22 | module.exports = { 23 | context: path.resolve(__dirname, '../'), 24 | entry: { 25 | app: './src/main.js' 26 | }, 27 | output: { 28 | path: config.build.assetsRoot, 29 | filename: '[name].js', 30 | publicPath: process.env.NODE_ENV === 'production' 31 | ? config.build.assetsPublicPath 32 | : config.dev.assetsPublicPath 33 | }, 34 | resolve: { 35 | extensions: ['.js', '.vue', '.json'], 36 | alias: { 37 | 'vue$': 'vue/dist/vue.esm.js', 38 | '@': resolve('src'), 39 | } 40 | }, 41 | module: { 42 | rules: [ 43 | ...(config.dev.useEslint ? [createLintingRule()] : []), 44 | { 45 | test: /\.vue$/, 46 | loader: 'vue-loader', 47 | options: vueLoaderConfig 48 | }, 49 | { 50 | test: /\.js$/, 51 | loader: 'babel-loader', 52 | include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] 53 | }, 54 | { 55 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 56 | loader: 'url-loader', 57 | options: { 58 | limit: 10000, 59 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 60 | } 61 | }, 62 | { 63 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 64 | loader: 'url-loader', 65 | options: { 66 | limit: 10000, 67 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 68 | } 69 | }, 70 | { 71 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 72 | loader: 'url-loader', 73 | options: { 74 | limit: 10000, 75 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 76 | } 77 | } 78 | ] 79 | }, 80 | node: { 81 | // prevent webpack from injecting useless setImmediate polyfill because Vue 82 | // source contains it (although only uses it if it's native). 83 | setImmediate: false, 84 | // prevent webpack from injecting mocks to Node native modules 85 | // that does not make sense for the client 86 | dgram: 'empty', 87 | fs: 'empty', 88 | net: 'empty', 89 | tls: 'empty', 90 | child_process: 'empty' 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /part2/front/book-manager/config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /part2/front/book-manager/config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.3.1 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | 10 | // Paths 11 | assetsSubDirectory: 'static', 12 | assetsPublicPath: '/', 13 | proxyTable: {}, 14 | 15 | // Various Dev Server settings 16 | host: 'localhost', // can be overwritten by process.env.HOST 17 | port: 8081, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 18 | autoOpenBrowser: false, 19 | errorOverlay: true, 20 | notifyOnErrors: true, 21 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 22 | 23 | // Use Eslint Loader? 24 | // If true, your code will be linted during bundling and 25 | // linting errors and warnings will be shown in the console. 26 | useEslint: true, 27 | // If true, eslint errors and warnings will also be shown in the error overlay 28 | // in the browser. 29 | showEslintErrorsInOverlay: false, 30 | 31 | /** 32 | * Source Maps 33 | */ 34 | 35 | // https://webpack.js.org/configuration/devtool/#development 36 | devtool: 'cheap-module-eval-source-map', 37 | 38 | // If you have problems debugging vue-files in devtools, 39 | // set this to false - it *may* help 40 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 41 | cacheBusting: true, 42 | 43 | cssSourceMap: true 44 | }, 45 | 46 | build: { 47 | // Template for index.html 48 | index: path.resolve(__dirname, '../dist/index.html'), 49 | 50 | // Paths 51 | assetsRoot: path.resolve(__dirname, '../dist'), 52 | assetsSubDirectory: 'static', 53 | assetsPublicPath: '/', 54 | 55 | /** 56 | * Source Maps 57 | */ 58 | 59 | productionSourceMap: true, 60 | // https://webpack.js.org/configuration/devtool/#production 61 | devtool: '#source-map', 62 | 63 | // Gzip off by default as many popular static hosts such as 64 | // Surge or Netlify already gzip all static assets for you. 65 | // Before setting to `true`, make sure to: 66 | // npm install --save-dev compression-webpack-plugin 67 | productionGzip: false, 68 | productionGzipExtensions: ['js', 'css'], 69 | 70 | // Run the build command with an extra argument to 71 | // View the bundle analyzer report after build finishes: 72 | // `npm run build --report` 73 | // Set to `true` or `false` to always turn it on or off 74 | bundleAnalyzerReport: process.env.npm_config_report 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /part2/front/book-manager/config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /part2/front/book-manager/config/test.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const devEnv = require('./dev.env') 4 | 5 | module.exports = merge(devEnv, { 6 | NODE_ENV: '"testing"' 7 | }) 8 | -------------------------------------------------------------------------------- /part2/front/book-manager/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | book-manager 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /part2/front/book-manager/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "book-manager", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "竹端尚人 ", 6 | "private": true, 7 | "scripts": { 8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", 9 | "start": "npm run dev", 10 | "unit": "jest --config test/unit/jest.conf.js --coverage", 11 | "e2e": "node test/e2e/runner.js", 12 | "test": "npm run unit && npm run e2e", 13 | "lint": "eslint --ext .js,.vue src test/unit test/e2e/specs", 14 | "build": "node build/build.js" 15 | }, 16 | "dependencies": { 17 | "axios": "^0.19.2", 18 | "axios-cookiejar-support": "^1.0.1", 19 | "vue": "^2.5.2", 20 | "vue-router": "^3.0.1" 21 | }, 22 | "devDependencies": { 23 | "autoprefixer": "^7.1.2", 24 | "babel-core": "^6.22.1", 25 | "babel-eslint": "^8.2.1", 26 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 27 | "babel-jest": "^21.0.2", 28 | "babel-loader": "^7.1.1", 29 | "babel-plugin-dynamic-import-node": "^1.2.0", 30 | "babel-plugin-syntax-jsx": "^6.18.0", 31 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", 32 | "babel-plugin-transform-runtime": "^6.22.0", 33 | "babel-plugin-transform-vue-jsx": "^3.5.0", 34 | "babel-preset-env": "^1.3.2", 35 | "babel-preset-stage-2": "^6.22.0", 36 | "babel-register": "^6.22.0", 37 | "chalk": "^2.0.1", 38 | "chromedriver": "^2.27.2", 39 | "copy-webpack-plugin": "^4.0.1", 40 | "cross-spawn": "^5.0.1", 41 | "css-loader": "^0.28.0", 42 | "eslint": "^4.15.0", 43 | "eslint-config-standard": "^10.2.1", 44 | "eslint-friendly-formatter": "^3.0.0", 45 | "eslint-loader": "^1.7.1", 46 | "eslint-plugin-import": "^2.7.0", 47 | "eslint-plugin-node": "^5.2.0", 48 | "eslint-plugin-promise": "^3.4.0", 49 | "eslint-plugin-standard": "^3.0.1", 50 | "eslint-plugin-vue": "^4.0.0", 51 | "extract-text-webpack-plugin": "^3.0.0", 52 | "file-loader": "^1.1.4", 53 | "friendly-errors-webpack-plugin": "^1.6.1", 54 | "html-webpack-plugin": "^2.30.1", 55 | "jest": "^22.0.4", 56 | "jest-serializer-vue": "^0.3.0", 57 | "nightwatch": "^0.9.12", 58 | "node-notifier": "^5.1.2", 59 | "optimize-css-assets-webpack-plugin": "^3.2.0", 60 | "ora": "^1.2.0", 61 | "portfinder": "^1.0.13", 62 | "postcss-import": "^11.0.0", 63 | "postcss-loader": "^2.0.8", 64 | "postcss-url": "^7.2.1", 65 | "rimraf": "^2.6.0", 66 | "selenium-server": "^3.0.1", 67 | "semver": "^5.3.0", 68 | "shelljs": "^0.7.6", 69 | "uglifyjs-webpack-plugin": "^1.1.1", 70 | "url-loader": "^0.5.8", 71 | "vue-jest": "^1.0.2", 72 | "vue-loader": "^13.3.0", 73 | "vue-style-loader": "^3.0.1", 74 | "vue-template-compiler": "^2.5.2", 75 | "webpack": "^3.6.0", 76 | "webpack-bundle-analyzer": "^2.9.0", 77 | "webpack-dev-server": "^2.9.1", 78 | "webpack-merge": "^4.1.0" 79 | }, 80 | "engines": { 81 | "node": ">= 6.0.0", 82 | "npm": ">= 3.0.0" 83 | }, 84 | "browserslist": [ 85 | "> 1%", 86 | "last 2 versions", 87 | "not ie <= 8" 88 | ] 89 | } 90 | -------------------------------------------------------------------------------- /part2/front/book-manager/src/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 | 24 | -------------------------------------------------------------------------------- /part2/front/book-manager/src/api/auth.js: -------------------------------------------------------------------------------- 1 | import { client } from './client' 2 | 3 | export default { 4 | login (email, password) { 5 | var params = new URLSearchParams() 6 | params.append('email', email) 7 | params.append('pass', password) 8 | 9 | return client.post('http://localhost:8080/login', params) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /part2/front/book-manager/src/api/book.js: -------------------------------------------------------------------------------- 1 | import { client } from './client' 2 | 3 | export default { 4 | getList () { 5 | return client 6 | .get('http://localhost:8080/book/list') 7 | .then(response => (response.data.book_list)) 8 | }, 9 | getDetail (id) { 10 | return client 11 | .get(`http://localhost:8080/book/detail/${id}`) 12 | .then(response => (response.data)) 13 | }, 14 | register (id, title, author, releaseDate) { 15 | return client.post('http://localhost:8080/admin/book/register', { 16 | id: id, 17 | title: title, 18 | author: author, 19 | release_date: releaseDate 20 | }) 21 | }, 22 | update (id, title, author, releaseDate) { 23 | return client.put('http://localhost:8080/admin/book/update', { 24 | id: id, 25 | title: title, 26 | author: author, 27 | release_date: releaseDate 28 | }) 29 | }, 30 | delete (id) { 31 | return client 32 | .delete(`http://localhost:8080/admin/book/delete/${id}`) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /part2/front/book-manager/src/api/client.js: -------------------------------------------------------------------------------- 1 | export const client = createClient() 2 | 3 | function createClient () { 4 | const Axios = require('axios') 5 | 6 | return Axios.create({ withCredentials: true }) 7 | } 8 | -------------------------------------------------------------------------------- /part2/front/book-manager/src/api/rental.js: -------------------------------------------------------------------------------- 1 | import { client } from './client' 2 | 3 | export default { 4 | startRental (id) { 5 | return client.post('http://localhost:8080/rental/start', { 6 | book_id: id 7 | }) 8 | }, 9 | endRental (id) { 10 | return client.delete(`http://localhost:8080/rental/end/${id}`) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /part2/front/book-manager/src/assets/css/base.css: -------------------------------------------------------------------------------- 1 | table.book { 2 | border: 1px solid #333; 3 | width: 500px; 4 | margin-left: auto; 5 | margin-right: auto; 6 | } 7 | 8 | table.register_book { 9 | border: 1px solid #333; 10 | width: 240px; 11 | margin-left: auto; 12 | margin-right: auto; 13 | } 14 | 15 | th.book { 16 | border: 1px solid #333; 17 | } 18 | 19 | td.book { 20 | border: 1px solid #333; 21 | } 22 | -------------------------------------------------------------------------------- /part2/front/book-manager/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-takehata/kotlin-server-side-programming-practice/3526abaf27718941f5d36bd2588ec126e9386ae5/part2/front/book-manager/src/assets/logo.png -------------------------------------------------------------------------------- /part2/front/book-manager/src/components/BookDeleteCompleted.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /part2/front/book-manager/src/components/BookDetail.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 86 | 87 | 90 | -------------------------------------------------------------------------------- /part2/front/book-manager/src/components/BookList.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 47 | 48 | 51 | -------------------------------------------------------------------------------- /part2/front/book-manager/src/components/BookRegister.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 48 | 49 | 52 | -------------------------------------------------------------------------------- /part2/front/book-manager/src/components/BookRegisterCompleted.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /part2/front/book-manager/src/components/BookUpdate.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 48 | 49 | 52 | -------------------------------------------------------------------------------- /part2/front/book-manager/src/components/BookUpdateCompleted.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /part2/front/book-manager/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 85 | 86 | 96 | 97 | 98 | 114 | -------------------------------------------------------------------------------- /part2/front/book-manager/src/components/Login.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 45 | 46 | 49 | -------------------------------------------------------------------------------- /part2/front/book-manager/src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import App from './App' 5 | import router from './router' 6 | 7 | Vue.config.productionTip = false 8 | 9 | /* eslint-disable no-new */ 10 | new Vue({ 11 | el: '#app', 12 | router, 13 | components: { App }, 14 | template: '' 15 | }) 16 | -------------------------------------------------------------------------------- /part2/front/book-manager/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import HelloWorld from '@/components/HelloWorld' 4 | import BookList from '@/components/BookList' 5 | import BookDetail from '@/components/BookDetail' 6 | import BookRegister from '@/components/BookRegister' 7 | import BookRegisterCompleted from '@/components/BookRegisterCompleted' 8 | import BookUpdate from '@/components/BookUpdate' 9 | import BookUpdateCompleted from '@/components/BookUpdateCompleted' 10 | import BookDeleteCompleted from '@/components/BookDeleteCompleted' 11 | import Login from '@/components/Login' 12 | 13 | Vue.use(Router) 14 | 15 | export default new Router({ 16 | mode: 'history', 17 | routes: [ 18 | { 19 | path: '/', 20 | name: 'HelloWorld', 21 | component: HelloWorld 22 | }, 23 | { 24 | path: '/book/list', 25 | name: 'BookList', 26 | component: BookList 27 | }, 28 | { 29 | path: '/book/detail/:book_id', 30 | name: 'BookDetail', 31 | component: BookDetail 32 | }, 33 | { 34 | path: '/admin/book/register', 35 | name: 'BookRegister', 36 | component: BookRegister 37 | }, 38 | { 39 | path: '/admin/book/register/completed', 40 | name: 'BookRegisterCompleted', 41 | component: BookRegisterCompleted 42 | }, 43 | { 44 | path: '/admin/book/update/completed', 45 | name: 'BookUpdateCompleted', 46 | component: BookUpdateCompleted 47 | }, 48 | { 49 | path: '/admin/book/update/:book_id', 50 | name: 'BookUpdate', 51 | component: BookUpdate 52 | }, 53 | { 54 | path: '/admin/book/delete/completed', 55 | name: 'BookDeleteCompleted', 56 | component: BookDeleteCompleted 57 | }, 58 | { 59 | path: '/login', 60 | name: 'Login', 61 | component: Login 62 | } 63 | ] 64 | }) 65 | -------------------------------------------------------------------------------- /part2/front/book-manager/static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-takehata/kotlin-server-side-programming-practice/3526abaf27718941f5d36bd2588ec126e9386ae5/part2/front/book-manager/static/.gitkeep -------------------------------------------------------------------------------- /part3/chapter09/README.md: -------------------------------------------------------------------------------- 1 | # 概要 2 | 「2 gRPCの導入」までの内容がgrpc-onlyディレクトリ、「3 Spring BootでgRPCのKotlinサーバーサイドプログラムを実装」の内容がgrpc-with-springbootディレクトリに入っています。 3 | 4 | それぞれのディレクトリでGradleプロジェクトとなっています。 5 | いずれもIntelliJ IDEAで [File]->[Open] を選択し、このディレクトリのbuild.gradle.ktsを開いてください。 6 | ダイアログが表示されるので、[Open as Project] を選択してください。 7 | 8 | # 実行方法 9 | ## grpc-only 10 | GreeterServer.ktのmain関数を実行してサーバーを起動し、GreeterClient.ktのmain関数を実行します。 11 | 12 | ## grpc-with-springboot 13 | 以下の方法で実行できます。 14 | 15 | - IntelliJ IDEAのGradleビューから、[Tasks]->[application]->[bootRun]を実行 16 | - ターミナルでdemoディレクトリから ```./gradlew bootRun``` を実行 17 | - Application.ktのmain関数を実行 18 | 19 | # 補足 20 | 複数のリストで同じクラス名や関数名を使っている場合など、並べて書かれているとコンパイルエラーになってしまうものは、コメントアウトされています。 21 | 必要に応じてコメントを外し、実行してください。 22 | コンパイルエラーになる例を紹介しているコードに関しても、同様にコメントアウトされています。 23 | -------------------------------------------------------------------------------- /part3/chapter09/grpc-only/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.google.protobuf.gradle.generateProtoTasks 2 | import com.google.protobuf.gradle.id 3 | import com.google.protobuf.gradle.plugins 4 | import com.google.protobuf.gradle.protobuf 5 | import com.google.protobuf.gradle.protoc 6 | 7 | plugins { 8 | kotlin("jvm") version "1.4.30" 9 | id("com.google.protobuf") version "0.8.15" 10 | id("idea") 11 | } 12 | 13 | repositories { 14 | gradlePluginPortal() 15 | jcenter() 16 | google() 17 | } 18 | 19 | dependencies { 20 | implementation(kotlin("stdlib")) 21 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2") 22 | 23 | implementation("io.grpc:grpc-kotlin-stub:1.0.0") 24 | implementation("io.grpc:grpc-netty:1.35.0") 25 | 26 | compileOnly("javax.annotation:javax.annotation-api:1.3.2") 27 | } 28 | 29 | protobuf { 30 | protoc { artifact = "com.google.protobuf:protoc:3.15.1" } 31 | plugins { 32 | id("grpc") { 33 | artifact = "io.grpc:protoc-gen-grpc-java:1.36.0" 34 | } 35 | id("grpckt") { 36 | artifact = "io.grpc:protoc-gen-grpc-kotlin:1.0.0:jdk7@jar" 37 | } 38 | } 39 | generateProtoTasks { 40 | all().forEach { 41 | it.plugins { 42 | id("grpc") 43 | id("grpckt") 44 | } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /part3/chapter09/grpc-only/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official -------------------------------------------------------------------------------- /part3/chapter09/grpc-only/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-takehata/kotlin-server-side-programming-practice/3526abaf27718941f5d36bd2588ec126e9386ae5/part3/chapter09/grpc-only/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part3/chapter09/grpc-only/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part3/chapter09/grpc-only/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "grpc-only" 2 | -------------------------------------------------------------------------------- /part3/chapter09/grpc-only/src/main/kotlin/example/greeter/client/GreeterClient.kt: -------------------------------------------------------------------------------- 1 | package example.greeter.client 2 | 3 | import example.greeter.GreeterGrpcKt 4 | import example.greeter.HelloRequest 5 | import io.grpc.ManagedChannelBuilder 6 | import kotlinx.coroutines.async 7 | import kotlinx.coroutines.runBlocking 8 | import java.util.concurrent.TimeUnit 9 | 10 | private const val HOST = "localhost" 11 | private const val PORT = 50051 12 | 13 | fun main() = runBlocking { 14 | val channel = ManagedChannelBuilder.forAddress(HOST, PORT) 15 | .usePlaintext() 16 | .build() 17 | 18 | try { 19 | val stub = GreeterGrpcKt.GreeterCoroutineStub(channel) 20 | 21 | val name = "Kotlin" 22 | val request = HelloRequest.newBuilder().setName(name).build() 23 | val response = async { stub.hello(request) } 24 | 25 | println("Response Text: ${response.await().text}") 26 | } finally { 27 | channel.shutdown().awaitTermination(5, TimeUnit.SECONDS) 28 | } 29 | } -------------------------------------------------------------------------------- /part3/chapter09/grpc-only/src/main/kotlin/example/greeter/server/GreeterServer.kt: -------------------------------------------------------------------------------- 1 | package example.greeter.server 2 | 3 | import io.grpc.ServerBuilder 4 | 5 | private const val PORT = 50051 6 | 7 | fun main() { 8 | val server = ServerBuilder 9 | .forPort(PORT) 10 | .addService(GreeterService()) 11 | .build() 12 | 13 | server.start() 14 | println("Started. port:$PORT") 15 | 16 | server.awaitTermination() 17 | } 18 | -------------------------------------------------------------------------------- /part3/chapter09/grpc-only/src/main/kotlin/example/greeter/server/GreeterService.kt: -------------------------------------------------------------------------------- 1 | package example.greeter.server 2 | 3 | import example.greeter.GreeterGrpcKt 4 | import example.greeter.HelloRequest 5 | import example.greeter.HelloResponse 6 | 7 | class GreeterService : GreeterGrpcKt.GreeterCoroutineImplBase() { 8 | override suspend fun hello(request: HelloRequest) = HelloResponse.newBuilder() 9 | .setText("Hello ${request.name}") 10 | .build() 11 | } 12 | -------------------------------------------------------------------------------- /part3/chapter09/grpc-only/src/main/proto/greeter.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package example.greeter; 4 | 5 | option java_multiple_files = true; 6 | 7 | service Greeter { 8 | rpc Hello (HelloRequest) returns (HelloResponse); 9 | } 10 | 11 | message HelloRequest { 12 | string name = 1; 13 | } 14 | 15 | message HelloResponse { 16 | string text = 1; 17 | } 18 | -------------------------------------------------------------------------------- /part3/chapter09/grpc-with-springboot/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | out/ 23 | !**/src/main/**/out/ 24 | !**/src/test/**/out/ 25 | 26 | ### NetBeans ### 27 | /nbproject/private/ 28 | /nbbuild/ 29 | /dist/ 30 | /nbdist/ 31 | /.nb-gradle/ 32 | 33 | ### VS Code ### 34 | .vscode/ 35 | -------------------------------------------------------------------------------- /part3/chapter09/grpc-with-springboot/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | import com.google.protobuf.gradle.generateProtoTasks 3 | import com.google.protobuf.gradle.id 4 | import com.google.protobuf.gradle.plugins 5 | import com.google.protobuf.gradle.protobuf 6 | import com.google.protobuf.gradle.protoc 7 | 8 | plugins { 9 | kotlin("jvm") version "1.4.30" 10 | kotlin("plugin.spring") version "1.4.30" 11 | id("org.springframework.boot") version "2.4.3" 12 | id("io.spring.dependency-management") version "1.0.11.RELEASE" 13 | id("com.google.protobuf") version "0.8.15" 14 | id("idea") 15 | } 16 | 17 | group = "com.example.grpc.kotlin" 18 | version = "0.0.1-SNAPSHOT" 19 | java.sourceCompatibility = JavaVersion.VERSION_11 20 | 21 | repositories { 22 | mavenCentral() 23 | } 24 | 25 | dependencies { 26 | implementation("org.springframework.boot:spring-boot-starter-web") 27 | implementation("com.fasterxml.jackson.module:jackson-module-kotlin") 28 | implementation("org.jetbrains.kotlin:kotlin-reflect") 29 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") 30 | implementation("io.grpc:grpc-kotlin-stub:1.0.0") 31 | implementation("io.grpc:grpc-netty:1.35.0") 32 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2") 33 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.4.2") 34 | implementation("io.github.lognet:grpc-spring-boot-starter:4.4.4") 35 | testImplementation("org.springframework.boot:spring-boot-starter-test") 36 | } 37 | 38 | tasks.withType { 39 | kotlinOptions { 40 | freeCompilerArgs = listOf("-Xjsr305=strict") 41 | jvmTarget = "11" 42 | } 43 | } 44 | 45 | protobuf { 46 | protoc { 47 | artifact = "com.google.protobuf:protoc:3.15.1" 48 | } 49 | plugins { 50 | id("grpc") { 51 | artifact = "io.grpc:protoc-gen-grpc-java:1.36.0" 52 | } 53 | id("grpckt") { 54 | artifact = "io.grpc:protoc-gen-grpc-kotlin:1.0.0:jdk7@jar" 55 | } 56 | } 57 | generateProtoTasks { 58 | all().forEach { 59 | it.plugins { 60 | id("grpc") 61 | id("grpckt") 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /part3/chapter09/grpc-with-springboot/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-takehata/kotlin-server-side-programming-practice/3526abaf27718941f5d36bd2588ec126e9386ae5/part3/chapter09/grpc-with-springboot/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part3/chapter09/grpc-with-springboot/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part3/chapter09/grpc-with-springboot/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "grpc-with-springboot" 2 | -------------------------------------------------------------------------------- /part3/chapter09/grpc-with-springboot/src/main/kotlin/com/example/grpc/springboot/Application.kt: -------------------------------------------------------------------------------- 1 | package com.example.grpc.springboot 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication 4 | import org.springframework.boot.runApplication 5 | 6 | @SpringBootApplication 7 | class GrpcWithSpringbootApplication 8 | 9 | fun main(args: Array) { 10 | runApplication(*args) 11 | } 12 | -------------------------------------------------------------------------------- /part3/chapter09/grpc-with-springboot/src/main/kotlin/com/example/grpc/springboot/client/GreeterClientController.kt: -------------------------------------------------------------------------------- 1 | package com.example.grpc.springboot.client 2 | 3 | import example.greeter.GreeterGrpcKt 4 | import example.greeter.HelloRequest 5 | import io.grpc.ManagedChannelBuilder 6 | import kotlinx.coroutines.async 7 | import kotlinx.coroutines.runBlocking 8 | import org.springframework.web.bind.annotation.GetMapping 9 | import org.springframework.web.bind.annotation.PathVariable 10 | import org.springframework.web.bind.annotation.RestController 11 | 12 | private const val HOST = "localhost" 13 | private const val PORT = 6565 14 | 15 | @RestController 16 | class GreeterClientController { 17 | @GetMapping("/greeter/hello/{name}") 18 | fun hello(@PathVariable name: String): String = runBlocking { 19 | val channel = ManagedChannelBuilder.forAddress(HOST, PORT) 20 | .usePlaintext() 21 | .build() 22 | 23 | val request = HelloRequest.newBuilder().setName(name).build() 24 | val stub = GreeterGrpcKt.GreeterCoroutineStub(channel) 25 | 26 | val response = async { stub.hello(request) } 27 | response.await().text 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /part3/chapter09/grpc-with-springboot/src/main/kotlin/com/example/grpc/springboot/server/GreeterService.kt: -------------------------------------------------------------------------------- 1 | package com.example.grpc.springboot.server 2 | 3 | import example.greeter.GreeterGrpcKt 4 | import example.greeter.HelloRequest 5 | import example.greeter.HelloResponse 6 | import org.lognet.springboot.grpc.GRpcService 7 | 8 | @GRpcService 9 | class GreeterService : GreeterGrpcKt.GreeterCoroutineImplBase() { 10 | override suspend fun hello(request: HelloRequest) = HelloResponse.newBuilder() 11 | .setText("Hello ${request.name}") 12 | .build() 13 | } -------------------------------------------------------------------------------- /part3/chapter09/grpc-with-springboot/src/main/proto/greeter.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package example.greeter; 4 | 5 | option java_multiple_files = true; 6 | 7 | service Greeter { 8 | rpc Hello (HelloRequest) returns (HelloResponse); 9 | } 10 | 11 | message HelloRequest { 12 | string name = 1; 13 | } 14 | 15 | message HelloResponse { 16 | string text = 1; 17 | } 18 | -------------------------------------------------------------------------------- /part3/chapter09/grpc-with-springboot/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /part3/chapter09/grpc-with-springboot/src/test/kotlin/com/example/grpc/springboot/ApplicationTests.kt: -------------------------------------------------------------------------------- 1 | package com.example.grpc.springboot 2 | 3 | import org.junit.jupiter.api.Test 4 | import org.springframework.boot.test.context.SpringBootTest 5 | 6 | @SpringBootTest 7 | class ApplicationTests { 8 | 9 | @Test 10 | fun contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part3/chapter10/.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle 2 | /.idea 3 | /out 4 | /build 5 | *.iml 6 | *.ipr 7 | *.iws 8 | -------------------------------------------------------------------------------- /part3/chapter10/README.md: -------------------------------------------------------------------------------- 1 | # 概要 2 | chapter10ディレクトリが、KtorアプリケーションのGradleプロジェクトとなっています。 3 | IntelliJ IDEAで [File]->[Open] を選択し、このディレクトリのbuild.gradle.ktsを開いてください。 4 | ダイアログが表示されるので、[Open as Project] を選択してください。 5 | 6 | 10章で作成した処理が全て入った状態のものになっています。 7 | 8 | # 実行方法 9 | 書籍内で解説しているものと同様、以下の方法で実行できます。 10 | 11 | - IntelliJ IDEAのGradleビューから、[Tasks]->[application]->[run]を実行 12 | - Application.ktのmain関数を実行 13 | 14 | # 補足 15 | 複数のリストで同じクラス名や関数名を使っている場合など、並べて書かれているとコンパイルエラーになってしまうものは、コメントアウトされています。 16 | 必要に応じてコメントを外し、実行してください。 17 | コンパイルエラーになる例を紹介しているコードに関しても、同様にコメントアウトされています。 -------------------------------------------------------------------------------- /part3/chapter10/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.dsl.Coroutines 2 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 3 | 4 | val ktor_version: String by project 5 | val kotlin_version: String by project 6 | val logback_version: String by project 7 | 8 | plugins { 9 | application 10 | kotlin("jvm") version "1.4.30" 11 | } 12 | 13 | group = "com.example" 14 | version = "0.0.1" 15 | 16 | application { 17 | mainClassName = "io.ktor.server.netty.EngineMain" 18 | } 19 | 20 | repositories { 21 | mavenLocal() 22 | jcenter() 23 | } 24 | 25 | dependencies { 26 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version") 27 | implementation("io.ktor:ktor-server-netty:$ktor_version") 28 | implementation("ch.qos.logback:logback-classic:$logback_version") 29 | implementation("io.ktor:ktor-locations:$ktor_version") 30 | implementation("io.ktor:ktor-jackson:$ktor_version") 31 | implementation("io.ktor:ktor-auth:$ktor_version") 32 | testImplementation("io.ktor:ktor-server-tests:$ktor_version") 33 | } 34 | 35 | kotlin.sourceSets["main"].kotlin.srcDirs("src") 36 | kotlin.sourceSets["test"].kotlin.srcDirs("test") 37 | 38 | sourceSets["main"].resources.srcDirs("resources") 39 | sourceSets["test"].resources.srcDirs("testresources") -------------------------------------------------------------------------------- /part3/chapter10/gradle.properties: -------------------------------------------------------------------------------- 1 | ktor_version=1.3.2 2 | kotlin.code.style=official 3 | kotlin_version=1.3.70 4 | logback_version=1.2.1 5 | -------------------------------------------------------------------------------- /part3/chapter10/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-takehata/kotlin-server-side-programming-practice/3526abaf27718941f5d36bd2588ec126e9386ae5/part3/chapter10/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part3/chapter10/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part3/chapter10/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 | -------------------------------------------------------------------------------- /part3/chapter10/resources/application.conf: -------------------------------------------------------------------------------- 1 | ktor { 2 | deployment { 3 | port = 8080 4 | port = ${?PORT} 5 | } 6 | application { 7 | modules = [ com.example.ApplicationKt.module ] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /part3/chapter10/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /part3/chapter10/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "chapter10" 2 | -------------------------------------------------------------------------------- /part3/chapter10/src/Application.kt: -------------------------------------------------------------------------------- 1 | package com.example 2 | 3 | import com.example.example.route.bookRoute 4 | import com.example.example.route.greetingRoute 5 | import com.example.example.route.userRoute 6 | import io.ktor.application.Application 7 | import io.ktor.application.call 8 | import io.ktor.application.install 9 | import io.ktor.auth.Authentication 10 | import io.ktor.auth.UserIdPrincipal 11 | import io.ktor.auth.authenticate 12 | import io.ktor.auth.authentication 13 | import io.ktor.auth.basic 14 | import io.ktor.features.ContentNegotiation 15 | import io.ktor.jackson.jackson 16 | import io.ktor.locations.Locations 17 | import io.ktor.response.respondText 18 | import io.ktor.routing.get 19 | import io.ktor.routing.routing 20 | 21 | fun main(args: Array): Unit = io.ktor.server.netty.EngineMain.main(args) 22 | 23 | @Suppress("unused") 24 | @kotlin.jvm.JvmOverloads 25 | fun Application.module(testing: Boolean = false) { 26 | install(Locations) 27 | 28 | install(ContentNegotiation) { 29 | jackson { 30 | // シリアライズ、デシリアライズ処理のカスタマイズを記述 31 | } 32 | } 33 | 34 | install(Authentication) { 35 | basic { 36 | validate { credentials -> 37 | if (credentials.name == "user" && credentials.password == "password") { 38 | UserIdPrincipal(credentials.name) 39 | } else { 40 | null 41 | } 42 | } 43 | } 44 | } 45 | 46 | routing { 47 | get("/") { 48 | call.respondText("Hello Ktor!") 49 | } 50 | 51 | get("/hello/{name}") { 52 | val name = call.parameters["name"] 53 | call.respondText("Hello $name!") 54 | } 55 | 56 | get("/hello") { 57 | val name = call.parameters["name"] 58 | call.respondText("Hello $name!") 59 | } 60 | 61 | greetingRoute() 62 | userRoute() 63 | bookRoute() 64 | authenticate { 65 | get("/authenticated") { 66 | val user = call.authentication.principal() 67 | call.respondText("authenticated id=${user?.name}") 68 | } 69 | } 70 | } 71 | } 72 | 73 | -------------------------------------------------------------------------------- /part3/chapter10/src/example/route/BookRoute.kt: -------------------------------------------------------------------------------- 1 | package com.example.example.route 2 | 3 | import io.ktor.application.call 4 | import io.ktor.locations.Location 5 | import io.ktor.locations.get 6 | import io.ktor.request.receive 7 | import io.ktor.response.respond 8 | import io.ktor.response.respondText 9 | import io.ktor.routing.Routing 10 | import io.ktor.routing.post 11 | import io.ktor.routing.route 12 | 13 | fun Routing.bookRoute() { 14 | route("/book") { 15 | @Location("/detail/{bookId}") 16 | data class BookLocation(val bookId: Long) 17 | get { request -> 18 | val response = BookResponse(request.bookId, "Kotlin入門", "Kotlin太郎") 19 | call.respond(response) 20 | } 21 | 22 | post("/register") { 23 | val request = call.receive() 24 | call.respondText("registered. id=${request.id} title=${request.title} author=${request.author}") 25 | } 26 | } 27 | } 28 | 29 | data class BookResponse( 30 | val id: Long, 31 | val title: String, 32 | val author: String 33 | ) 34 | 35 | data class RegisterRequest( 36 | val id: Long, 37 | val title: String, 38 | val author: String 39 | ) 40 | -------------------------------------------------------------------------------- /part3/chapter10/src/example/route/GreetingRoute.kt: -------------------------------------------------------------------------------- 1 | package com.example.example.route 2 | 3 | import io.ktor.application.call 4 | import io.ktor.response.respondText 5 | import io.ktor.routing.Routing 6 | import io.ktor.routing.get 7 | import io.ktor.routing.route 8 | 9 | fun Routing.greetingRoute() { 10 | get("/") { 11 | call.respondText("Hello Ktor!") 12 | } 13 | 14 | route("/greeting") { 15 | get("/hello") { 16 | // ・・・ 17 | } 18 | 19 | get("/goodmorning") { 20 | // ・・・ 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /part3/chapter10/src/example/route/UserRoute.kt: -------------------------------------------------------------------------------- 1 | package com.example.example.route 2 | 3 | import io.ktor.application.call 4 | import io.ktor.auth.Principal 5 | import io.ktor.auth.UserIdPrincipal 6 | import io.ktor.auth.authentication 7 | import io.ktor.locations.Location 8 | import io.ktor.locations.get 9 | import io.ktor.response.respondText 10 | import io.ktor.routing.Route 11 | import io.ktor.routing.Routing 12 | import io.ktor.routing.get 13 | 14 | //@Location("/user/{id}") 15 | //data class GetUserLocation(val id: Long) 16 | 17 | @Location("/user") 18 | class UserLocation { 19 | @Location("/{id}") 20 | data class GetLocation(val id: Long) 21 | 22 | @Location("/detail/{id}") 23 | data class GetDetailLocation(val id: Long) 24 | 25 | @Location("/authenticated") 26 | data class AuthenticatedLocation(val id: Long) 27 | } 28 | 29 | fun Routing.userRoute() { 30 | // get { param -> 31 | // val id = param.id 32 | // call.respondText("id=$id") 33 | // } 34 | 35 | get { param -> 36 | val id = param.id 37 | call.respondText("get id=$id") 38 | } 39 | 40 | get { param -> 41 | val id = param.id 42 | call.respondText("getDetail id=$id") 43 | } 44 | } 45 | 46 | fun Route.authenticatedUserRoute() { 47 | get("/authenticated") { 48 | val user = call.authentication.principal() 49 | call.respondText("authenticated id=${user?.name}") 50 | } 51 | } 52 | 53 | data class MyUserPrincipal(val id: Long, val name: String, val profile: String) : Principal -------------------------------------------------------------------------------- /part3/chapter11/README.md: -------------------------------------------------------------------------------- 1 | # 概要 2 | chapter11ディレクトリが、一つのGradleプロジェクトとなっています。 3 | IntelliJ IDEAで [File]->[Open] を選択し、このディレクトリのbuild.gradle.ktsを開いてください。 4 | ダイアログが表示されるので、[Open as Project] を選択してください。 5 | 6 | 「3 DSLとDAOそれぞれの実装方法」の内容がDslExample.ktとDaoExample.kt、「4 DAOでCRUDを作成する」の内容がCrudExample.ktに入っています。 7 | CrudExample.ktは、各コード上の書籍との対応を以下のようにリスト番号をコメントで記載しています。 8 | 9 | ``` 10 | /** 11 | * リスト11.4.2 12 | */ 13 | ``` 14 | 15 | # 実行方法 16 | ## DslExample.kt、DaoExample.kt 17 | それぞれmain関数を実行します。 18 | 19 | ## CrudExample.kt 20 | クラスの定義などを除く、実行結果の伴うサンプルコードは、リストごとに関数として処理を記述しています。 21 | 一番上にあるmain関数を以下のように書き換え、実行したいリストの関数を呼び出す形に変更することで、動作確認をできます。 22 | 23 | ``` 24 | fun main() { 25 | list11_4_5() 26 | } 27 | ``` 28 | 29 | # 補足 30 | 複数のリストで同じクラス名や関数名を使っている場合など、並べて書かれているとコンパイルエラーになってしまうものは、コメントアウトされています。 31 | 必要に応じてコメントを外し、実行してください。 32 | コンパイルエラーになる例を紹介しているコードに関しても、同様にコメントアウトされています。 33 | -------------------------------------------------------------------------------- /part3/chapter11/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | plugins { 4 | kotlin("jvm") version "1.4.30" 5 | } 6 | 7 | group = "me.take7010" 8 | version = "1.0-SNAPSHOT" 9 | 10 | repositories { 11 | mavenCentral() 12 | jcenter() 13 | } 14 | 15 | dependencies { 16 | implementation("org.jetbrains.exposed:exposed-core:0.29.1") 17 | implementation("org.jetbrains.exposed:exposed-dao:0.29.1") 18 | implementation("org.jetbrains.exposed:exposed-jdbc:0.29.1") 19 | implementation("mysql:mysql-connector-java:8.0.23") 20 | } 21 | 22 | tasks.withType() { 23 | kotlinOptions.jvmTarget = "11" 24 | } -------------------------------------------------------------------------------- /part3/chapter11/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official -------------------------------------------------------------------------------- /part3/chapter11/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-takehata/kotlin-server-side-programming-practice/3526abaf27718941f5d36bd2588ec126e9386ae5/part3/chapter11/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part3/chapter11/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part3/chapter11/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven("https://dl.bintray.com/kotlin/kotlin-eap") 4 | 5 | mavenCentral() 6 | 7 | maven("https://plugins.gradle.org/m2/") 8 | } 9 | } 10 | rootProject.name = "chapter11" 11 | 12 | -------------------------------------------------------------------------------- /part3/chapter11/src/main/kotlin/CrudExample.kt: -------------------------------------------------------------------------------- 1 | import org.jetbrains.exposed.sql.Database 2 | import org.jetbrains.exposed.sql.transactions.transaction 3 | 4 | fun main() { 5 | list11_4_4() 6 | } 7 | 8 | /** 9 | * リスト11.4.2 10 | */ 11 | fun createSessionFactory() { 12 | Database.connect( 13 | url = "jdbc:mysql://127.0.0.1:3306/exposed_example", 14 | driver = "com.mysql.jdbc.Driver", 15 | user = "root", 16 | password = "mysql" 17 | ) 18 | } 19 | 20 | /** 21 | * リスト11.4.4 22 | */ 23 | fun list11_4_4() { 24 | createSessionFactory() 25 | transaction { 26 | val list = MemberEntity.all().map { MemberModel(it) } 27 | list.forEach { 28 | println(it) 29 | } 30 | } 31 | } 32 | 33 | /** 34 | * リスト11.4.5 35 | */ 36 | fun list11_4_5() { 37 | createSessionFactory() 38 | transaction { 39 | val entity = MemberEntity.findById(2) 40 | entity?.let { println(MemberModel(it)) } 41 | } 42 | } 43 | 44 | /** 45 | * リスト11.4.6 46 | */ 47 | fun list11_4_6() { 48 | createSessionFactory() 49 | transaction { 50 | val entity = MemberEntity.find { MemberTable.name eq "Saburo" }.map { MemberModel(it) } 51 | entity?.let { println(it) } 52 | } 53 | } 54 | 55 | /** 56 | * リスト11.4.7 57 | */ 58 | fun list11_4_7() { 59 | createSessionFactory() 60 | transaction { 61 | val entity = MemberEntity.new { 62 | name = "Shiro" 63 | } 64 | println(MemberModel(entity)) 65 | } 66 | } 67 | 68 | /** 69 | * リスト11.4.8 70 | */ 71 | fun list11_4_8() { 72 | createSessionFactory() 73 | transaction { 74 | val entity = MemberEntity.findById(4) 75 | entity?.let { it.name = "Yonro" } 76 | } 77 | } 78 | 79 | /** 80 | * リスト11.4.10 81 | */ 82 | fun list11_4_10() { 83 | createSessionFactory() 84 | transaction { 85 | val entity = MemberEntity.findById(4) 86 | entity?.let { it.delete() } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /part3/chapter11/src/main/kotlin/DaoExample.kt: -------------------------------------------------------------------------------- 1 | import org.jetbrains.exposed.dao.IntEntity 2 | import org.jetbrains.exposed.dao.IntEntityClass 3 | import org.jetbrains.exposed.dao.id.EntityID 4 | import org.jetbrains.exposed.dao.id.IntIdTable 5 | import org.jetbrains.exposed.sql.Database 6 | import org.jetbrains.exposed.sql.StdOutSqlLogger 7 | import org.jetbrains.exposed.sql.addLogger 8 | import org.jetbrains.exposed.sql.transactions.transaction 9 | 10 | fun main() { 11 | Database.connect( 12 | "jdbc:mysql://127.0.0.1:3306/exposed_example", 13 | driver = "com.mysql.jdbc.Driver", 14 | user = "root", 15 | password = "mysql" 16 | ) 17 | 18 | transaction { 19 | addLogger(StdOutSqlLogger) 20 | 21 | val member = MemberEntity.new { 22 | name = "Kotlin" 23 | } 24 | println("Inserted id: ${member.id}") 25 | 26 | MemberEntity.findById(member.id)?.let { 27 | println("id: ${it.id}") 28 | println("name: ${it.name}") 29 | } 30 | } 31 | } 32 | 33 | object MemberTable : IntIdTable("member") { 34 | val name = varchar("name", 32) 35 | } 36 | 37 | class MemberEntity(id: EntityID) : IntEntity(id) { 38 | companion object : IntEntityClass(MemberTable) 39 | var name by MemberTable.name 40 | } -------------------------------------------------------------------------------- /part3/chapter11/src/main/kotlin/DslExample.kt: -------------------------------------------------------------------------------- 1 | import org.jetbrains.exposed.sql.Database 2 | import org.jetbrains.exposed.sql.StdOutSqlLogger 3 | import org.jetbrains.exposed.sql.Table 4 | import org.jetbrains.exposed.sql.addLogger 5 | import org.jetbrains.exposed.sql.insert 6 | import org.jetbrains.exposed.sql.select 7 | import org.jetbrains.exposed.sql.transactions.transaction 8 | 9 | fun main() { 10 | Database.connect( 11 | "jdbc:mysql://127.0.0.1:3306/exposed_example", 12 | driver = "com.mysql.jdbc.Driver", 13 | user = "root", 14 | password = "mysql" 15 | ) 16 | 17 | transaction { 18 | addLogger(StdOutSqlLogger) 19 | 20 | val id = Member.insert { 21 | it[name] = "Kotlin" 22 | } get Member.id 23 | println("Inserted id: $id") 24 | 25 | val member = Member.select { Member.id eq id }.single() 26 | println("id: ${member[Member.id]}") 27 | println("name: ${member[Member.name]}") 28 | } 29 | } 30 | 31 | object Member : Table("member") { 32 | val id = integer("id").autoIncrement() 33 | val name = varchar("name", 32) 34 | } -------------------------------------------------------------------------------- /part3/chapter11/src/main/kotlin/Member.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * リスト11.4.3 3 | */ 4 | data class MemberModel(val id: Int, val name: String) { 5 | constructor(entity: MemberEntity) : this(entity.id.value, entity.name) 6 | } 7 | -------------------------------------------------------------------------------- /part3/chapter12/README.md: -------------------------------------------------------------------------------- 1 | # 概要 2 | chapter12ディレクトリが、一つのGradleプロジェクトとなっています。 3 | IntelliJ IDEAで [File]->[Open] を選択し、このディレクトリのbuild.gradle.ktsを開いてください。 4 | ダイアログが表示されるので、[Open as Project] を選択してください。 5 | 6 | 12章で作成した処理が全て入った状態のものになっています。 7 | 8 | # 実行方法 9 | 書籍内で解説しているものと同様、各テストクラスのテストケースの関数を実行することで、動作確認をできます。 10 | 11 | # 補足 12 | 複数のリストで同じクラス名や関数名を使っている場合など、並べて書かれているとコンパイルエラーになってしまうものは、コメントアウトされています。 13 | 必要に応じてコメントを外し、実行してください。 14 | コンパイルエラーになる例を紹介しているコードに関しても、同様にコメントアウトされています。 15 | -------------------------------------------------------------------------------- /part3/chapter12/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | plugins { 4 | kotlin("jvm") version "1.4.30" 5 | } 6 | 7 | group = "me.take7010" 8 | version = "1.0-SNAPSHOT" 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | testImplementation("io.kotest:kotest-runner-junit5-jvm:4.4.3") 16 | testImplementation("io.mockk:mockk:1.10.6") 17 | } 18 | 19 | tasks.withType() { 20 | kotlinOptions.jvmTarget = "11" 21 | } 22 | 23 | tasks.test { 24 | useJUnitPlatform() 25 | } -------------------------------------------------------------------------------- /part3/chapter12/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official -------------------------------------------------------------------------------- /part3/chapter12/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-takehata/kotlin-server-side-programming-practice/3526abaf27718941f5d36bd2588ec126e9386ae5/part3/chapter12/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part3/chapter12/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part3/chapter12/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "chapter12" 2 | 3 | -------------------------------------------------------------------------------- /part3/chapter12/src/main/kotlin/example/kotest/Number.kt: -------------------------------------------------------------------------------- 1 | package example.kotest 2 | 3 | data class Number(val value: Int) { 4 | fun isOdd(): Boolean { 5 | return value % 2 == 1 6 | } 7 | 8 | fun isRange(min: Int, max: Int): Boolean { 9 | return value in min..max 10 | } 11 | } -------------------------------------------------------------------------------- /part3/chapter12/src/main/kotlin/example/kotest/UserRepository.kt: -------------------------------------------------------------------------------- 1 | package example.kotest 2 | 3 | class UserRepository { 4 | fun findName(id: Int): String? { 5 | // Dummy logic 6 | return "Kotlin" 7 | } 8 | } -------------------------------------------------------------------------------- /part3/chapter12/src/main/kotlin/example/kotest/UserService.kt: -------------------------------------------------------------------------------- 1 | package example.kotest 2 | 3 | class UserService(private val userRepository: UserRepository) { 4 | fun createMessage(id: Int): String? { 5 | if (id < 0) return null 6 | return userRepository.findName(id)?.let { "Hello $it" } 7 | } 8 | } -------------------------------------------------------------------------------- /part3/chapter12/src/test/kotlin/example/kotest/NumberTestByAnnotationSpec.kt: -------------------------------------------------------------------------------- 1 | package example.kotest 2 | 3 | import io.kotest.core.spec.style.AnnotationSpec 4 | import io.kotest.matchers.shouldBe 5 | 6 | class NumberTestByAnnotationSpec : AnnotationSpec() { 7 | @Test 8 | fun `isOdd when value is odd number then return true`() { 9 | val number = Number(1) 10 | number.isOdd() shouldBe true 11 | } 12 | 13 | @Test 14 | fun `isOdd when value is even number then return false`() { 15 | val number = Number(2) 16 | number.isOdd() shouldBe false 17 | } 18 | } -------------------------------------------------------------------------------- /part3/chapter12/src/test/kotlin/example/kotest/NumberTestByBehaviorSpec.kt: -------------------------------------------------------------------------------- 1 | package example.kotest 2 | 3 | import io.kotest.core.spec.style.BehaviorSpec 4 | import io.kotest.matchers.shouldBe 5 | 6 | class NumberTestByBehaviorSpec : BehaviorSpec() { 7 | init { 8 | given("isOdd") { 9 | `when`("num is odd number") { 10 | val number = Number(1) 11 | then("return true") { 12 | number.isOdd() shouldBe true 13 | } 14 | } 15 | 16 | `when`("num is even number") { 17 | val number = Number(2) 18 | then("return false") { 19 | number.isOdd() shouldBe false 20 | } 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /part3/chapter12/src/test/kotlin/example/kotest/NumberTestByStringSpec.kt: -------------------------------------------------------------------------------- 1 | package example.kotest 2 | 3 | import io.kotest.core.spec.style.StringSpec 4 | import io.kotest.data.forAll 5 | import io.kotest.data.row 6 | import io.kotest.matchers.shouldBe 7 | import io.kotest.data.headers 8 | import io.kotest.data.table 9 | 10 | class NumberTestByStringSpec : StringSpec() { 11 | init { 12 | "isOdd:: when value is odd number then return true" { 13 | val number = Number(1) 14 | number.isOdd() shouldBe true 15 | } 16 | 17 | "isOdd:: when value is even number then return false" { 18 | val number = Number(2) 19 | number.isOdd() shouldBe false 20 | } 21 | 22 | "isRange:: when value in range then return true" { 23 | forAll( 24 | table( 25 | headers("value"), 26 | row(1), 27 | row(10) 28 | ) 29 | ) { value -> 30 | val number = Number(value) 31 | number.isRange(1, 10) shouldBe true 32 | } 33 | } 34 | 35 | "isRange:: when value not in range then return false" { 36 | forAll( 37 | table( 38 | headers("value"), 39 | row(0), 40 | row(11) 41 | ) 42 | ) { value -> 43 | val number = Number(value) 44 | number.isRange(1, 10) shouldBe false 45 | } 46 | } 47 | 48 | "isRange:: when value is minimum then return true" { 49 | val number = Number(1) 50 | number.isRange(1, 10) shouldBe true 51 | } 52 | 53 | "isRange:: when value is maximum then return true" { 54 | val number = Number(10) 55 | number.isRange(1, 10) shouldBe true 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /part3/chapter12/src/test/kotlin/example/kotest/UserServiceTest.kt: -------------------------------------------------------------------------------- 1 | package example.kotest 2 | 3 | import io.kotest.core.spec.style.StringSpec 4 | import io.kotest.matchers.shouldBe 5 | import io.mockk.every 6 | import io.mockk.mockk 7 | import io.mockk.verify 8 | 9 | class UserServiceTest : StringSpec() { 10 | init { 11 | "createMessage:: when user name is exist then return message" { 12 | val userRepository = mockk() 13 | val target = UserService(userRepository) 14 | 15 | val id = 100 16 | 17 | every { userRepository.findName(any()) } returns "Kotest" 18 | 19 | val result = target.createMessage(id) 20 | 21 | result shouldBe "Hello Kotest" 22 | verify { userRepository.findName(id) } 23 | } 24 | 25 | "createMessage:: when user name is not exist then return null" { 26 | val userRepository = mockk() 27 | val target = UserService(userRepository) 28 | 29 | val id = 100 30 | 31 | every { userRepository.findName(any()) } returns null 32 | 33 | val result = target.createMessage(id) 34 | 35 | result shouldBe null 36 | verify { userRepository.findName(id) } 37 | } 38 | 39 | "createMessage:: when id less than 0 then return null" { 40 | val userRepository = mockk() 41 | val target = UserService(userRepository) 42 | 43 | val id = -1 44 | 45 | every { userRepository.findName(any()) } returns null 46 | 47 | val result = target.createMessage(id) 48 | 49 | result shouldBe null 50 | verify(exactly = 0) { userRepository.findName(any()) } 51 | } 52 | } 53 | } --------------------------------------------------------------------------------