├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.adoc ├── collaborative-sd ├── images │ ├── asciidoc-readme.png │ ├── git-branches.png │ ├── git-changes.png │ ├── github-commits.png │ ├── github-issues.png │ ├── github-labels.png │ ├── github-pr-changes.png │ ├── github-pr.png │ ├── github-project.png │ ├── github-resolved-issues.png │ ├── graphics.key │ ├── travis-build.png │ └── travis-history.png └── index.adoc ├── ddd-and-spring ├── images │ ├── bounded-context.png │ ├── bounded-context1.png │ ├── ddd-building-blocks.png │ ├── ddd-quickly.png │ └── model.png └── index.adoc ├── deploy.sh ├── frameworks-and-libraries ├── images │ ├── project.png │ ├── sts-components.png │ └── types.png ├── index.adoc ├── pom.xml ├── quickstart │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── demo │ │ │ │ ├── Application.java │ │ │ │ └── WelcomeController.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── demo │ │ └── ApplicationTests.java └── spring │ ├── pom.xml │ └── src │ ├── main │ └── java │ │ └── example │ │ ├── complete │ │ ├── Application.java │ │ ├── Customer.java │ │ ├── CustomerManagement.java │ │ ├── CustomerRepository.java │ │ ├── DefaultCustomerManagement.java │ │ └── JpaCustomerRepository.java │ │ └── initial │ │ ├── Customer.java │ │ ├── CustomerManagement.java │ │ ├── CustomerRepository.java │ │ └── DefaultCustomerManagement.java │ └── test │ └── java │ └── example │ ├── complete │ ├── CustomerUnitTests.java │ ├── DefaultCustomerManagementIntegrationTests.java │ ├── DefaultCustomerManagementUnitTests.java │ └── InMemoryCustomerRepository.java │ └── initial │ ├── CustomerUnitTests.java │ ├── DefaultCustomerManagementIntegrationTests.java │ └── DefaultCustomerManagementUnitTests.java ├── index └── index.adoc ├── java-tooling ├── images │ ├── breakpoints_view.gif │ ├── console_view.png │ ├── debug_view.gif │ ├── display_view.gif │ ├── eclipse-maven-dependency-hierarchy.png │ ├── eclipse-maven-sources.png │ ├── junit_view.gif │ ├── moneta-repository.png │ ├── new-maven-project.png │ └── variable_view.gif └── index.adoc ├── layout ├── .gitignore ├── css │ ├── lectures.css │ └── spring.css └── js │ ├── toc.js │ └── tocbot │ ├── tocbot.css │ └── tocbot.min.js ├── overview └── intro.pdf ├── pom.xml ├── render.sh ├── scripts └── prepare.sh ├── spring-webapps-old ├── README.adoc ├── images │ ├── maven-console.png │ ├── project.png │ └── sts-components.png ├── index.adoc ├── quickstart.adoc └── spring.adoc ├── spring-webapps ├── README.adoc ├── code │ ├── .gitignore │ ├── complete │ │ ├── pom.xml │ │ └── src │ │ │ ├── main │ │ │ ├── java │ │ │ │ └── example │ │ │ │ │ ├── Application.java │ │ │ │ │ ├── Greeter.java │ │ │ │ │ └── MyController.java │ │ │ └── resources │ │ │ │ ├── .gitignore │ │ │ │ ├── application.properties │ │ │ │ └── templates │ │ │ │ └── hello.html │ │ │ └── test │ │ │ └── java │ │ │ └── example │ │ │ └── GreeterUnitTests.java │ ├── initial │ │ ├── pom.xml │ │ └── src │ │ │ ├── main │ │ │ ├── java │ │ │ │ └── example │ │ │ │ │ └── Application.java │ │ │ └── resources │ │ │ │ ├── .gitignore │ │ │ │ └── application.properties │ │ │ └── test │ │ │ └── java │ │ │ └── example │ │ │ └── package-info.java │ ├── mvnw │ ├── mvnw.cmd │ └── pom.xml ├── images │ ├── images.key │ └── lifecycle.png └── index.adoc └── spring ├── .gitignore └── index.adoc /.gitignore: -------------------------------------------------------------------------------- 1 | js/ 2 | css/ 3 | images/graphics/*.png 4 | .classpath 5 | .project 6 | target/ 7 | .settings/ 8 | *.html 9 | *.cache 10 | diag-*.png 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | before_install: 4 | - gem install asciidoctor 5 | 6 | script: 7 | - "./render.sh" 8 | 9 | after_success: 10 | # Upload generated files to FTP server 11 | - find . -type f \( -name "*.html" -o -name "*.png" -o -name "*.gif" -o -name "*.pdf" \) -exec echo {} \; -exec curl --ftp-create-dirs -T {} -u $FTP_USER:$FTP_PASS ftp://static.olivergierke.de/lectures/{} \; 12 | 13 | env: 14 | global: 15 | - secure: "KkwMqESH05/zbi7CaEAdkEfnq9qQVifTDFWxCLDA/yC4zd+/uW5WshTuJpOyMV85WoCbqj08liU1EX7k2AO3biv77NLty6qKDqTG/QjHDKcKpWdtOe17kBcqXou5aHwQuonI2hY/eoPsuZKlghMPx+lkri+xZZecmhvC/Jp3ovE=" 16 | - secure: "YBVk3dAqhtR8jGFVCHuu1zLAeyTYSL0FQZElC30/C6vZi1YOY/HRKFd9vjC/mTFHp1sMoesXgB2rtVpU6S0q2D00D8tdaN1LvrtHNfiY73VeTqDmMkU9wUDci6i6Iubv8mIOpP7Csa8vR54+PWaEj3r35NxGpRBU/KMy/J57bKE=" 17 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Attribution-NonCommercial-ShareAlike 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More_considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International 58 | Public License 59 | 60 | By exercising the Licensed Rights (defined below), You accept and agree 61 | to be bound by the terms and conditions of this Creative Commons 62 | Attribution-NonCommercial-ShareAlike 4.0 International Public License 63 | ("Public License"). To the extent this Public License may be 64 | interpreted as a contract, You are granted the Licensed Rights in 65 | consideration of Your acceptance of these terms and conditions, and the 66 | Licensor grants You such rights in consideration of benefits the 67 | Licensor receives from making the Licensed Material available under 68 | these terms and conditions. 69 | 70 | 71 | Section 1 -- Definitions. 72 | 73 | a. Adapted Material means material subject to Copyright and Similar 74 | Rights that is derived from or based upon the Licensed Material 75 | and in which the Licensed Material is translated, altered, 76 | arranged, transformed, or otherwise modified in a manner requiring 77 | permission under the Copyright and Similar Rights held by the 78 | Licensor. For purposes of this Public License, where the Licensed 79 | Material is a musical work, performance, or sound recording, 80 | Adapted Material is always produced where the Licensed Material is 81 | synched in timed relation with a moving image. 82 | 83 | b. Adapter's License means the license You apply to Your Copyright 84 | and Similar Rights in Your contributions to Adapted Material in 85 | accordance with the terms and conditions of this Public License. 86 | 87 | c. BY-NC-SA Compatible License means a license listed at 88 | creativecommons.org/compatiblelicenses, approved by Creative 89 | Commons as essentially the equivalent of this Public License. 90 | 91 | d. Copyright and Similar Rights means copyright and/or similar rights 92 | closely related to copyright including, without limitation, 93 | performance, broadcast, sound recording, and Sui Generis Database 94 | Rights, without regard to how the rights are labeled or 95 | categorized. For purposes of this Public License, the rights 96 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 97 | Rights. 98 | 99 | e. Effective Technological Measures means those measures that, in the 100 | absence of proper authority, may not be circumvented under laws 101 | fulfilling obligations under Article 11 of the WIPO Copyright 102 | Treaty adopted on December 20, 1996, and/or similar international 103 | agreements. 104 | 105 | f. Exceptions and Limitations means fair use, fair dealing, and/or 106 | any other exception or limitation to Copyright and Similar Rights 107 | that applies to Your use of the Licensed Material. 108 | 109 | g. License Elements means the license attributes listed in the name 110 | of a Creative Commons Public License. The License Elements of this 111 | Public License are Attribution, NonCommercial, and ShareAlike. 112 | 113 | h. Licensed Material means the artistic or literary work, database, 114 | or other material to which the Licensor applied this Public 115 | License. 116 | 117 | i. Licensed Rights means the rights granted to You subject to the 118 | terms and conditions of this Public License, which are limited to 119 | all Copyright and Similar Rights that apply to Your use of the 120 | Licensed Material and that the Licensor has authority to license. 121 | 122 | j. Licensor means the individual(s) or entity(ies) granting rights 123 | under this Public License. 124 | 125 | k. NonCommercial means not primarily intended for or directed towards 126 | commercial advantage or monetary compensation. For purposes of 127 | this Public License, the exchange of the Licensed Material for 128 | other material subject to Copyright and Similar Rights by digital 129 | file-sharing or similar means is NonCommercial provided there is 130 | no payment of monetary compensation in connection with the 131 | exchange. 132 | 133 | l. Share means to provide material to the public by any means or 134 | process that requires permission under the Licensed Rights, such 135 | as reproduction, public display, public performance, distribution, 136 | dissemination, communication, or importation, and to make material 137 | available to the public including in ways that members of the 138 | public may access the material from a place and at a time 139 | individually chosen by them. 140 | 141 | m. Sui Generis Database Rights means rights other than copyright 142 | resulting from Directive 96/9/EC of the European Parliament and of 143 | the Council of 11 March 1996 on the legal protection of databases, 144 | as amended and/or succeeded, as well as other essentially 145 | equivalent rights anywhere in the world. 146 | 147 | n. You means the individual or entity exercising the Licensed Rights 148 | under this Public License. Your has a corresponding meaning. 149 | 150 | 151 | Section 2 -- Scope. 152 | 153 | a. License grant. 154 | 155 | 1. Subject to the terms and conditions of this Public License, 156 | the Licensor hereby grants You a worldwide, royalty-free, 157 | non-sublicensable, non-exclusive, irrevocable license to 158 | exercise the Licensed Rights in the Licensed Material to: 159 | 160 | a. reproduce and Share the Licensed Material, in whole or 161 | in part, for NonCommercial purposes only; and 162 | 163 | b. produce, reproduce, and Share Adapted Material for 164 | NonCommercial purposes only. 165 | 166 | 2. Exceptions and Limitations. For the avoidance of doubt, where 167 | Exceptions and Limitations apply to Your use, this Public 168 | License does not apply, and You do not need to comply with 169 | its terms and conditions. 170 | 171 | 3. Term. The term of this Public License is specified in Section 172 | 6(a). 173 | 174 | 4. Media and formats; technical modifications allowed. The 175 | Licensor authorizes You to exercise the Licensed Rights in 176 | all media and formats whether now known or hereafter created, 177 | and to make technical modifications necessary to do so. The 178 | Licensor waives and/or agrees not to assert any right or 179 | authority to forbid You from making technical modifications 180 | necessary to exercise the Licensed Rights, including 181 | technical modifications necessary to circumvent Effective 182 | Technological Measures. For purposes of this Public License, 183 | simply making modifications authorized by this Section 2(a) 184 | (4) never produces Adapted Material. 185 | 186 | 5. Downstream recipients. 187 | 188 | a. Offer from the Licensor -- Licensed Material. Every 189 | recipient of the Licensed Material automatically 190 | receives an offer from the Licensor to exercise the 191 | Licensed Rights under the terms and conditions of this 192 | Public License. 193 | 194 | b. Additional offer from the Licensor -- Adapted Material. 195 | Every recipient of Adapted Material from You 196 | automatically receives an offer from the Licensor to 197 | exercise the Licensed Rights in the Adapted Material 198 | under the conditions of the Adapter's License You apply. 199 | 200 | c. No downstream restrictions. You may not offer or impose 201 | any additional or different terms or conditions on, or 202 | apply any Effective Technological Measures to, the 203 | Licensed Material if doing so restricts exercise of the 204 | Licensed Rights by any recipient of the Licensed 205 | Material. 206 | 207 | 6. No endorsement. Nothing in this Public License constitutes or 208 | may be construed as permission to assert or imply that You 209 | are, or that Your use of the Licensed Material is, connected 210 | with, or sponsored, endorsed, or granted official status by, 211 | the Licensor or others designated to receive attribution as 212 | provided in Section 3(a)(1)(A)(i). 213 | 214 | b. Other rights. 215 | 216 | 1. Moral rights, such as the right of integrity, are not 217 | licensed under this Public License, nor are publicity, 218 | privacy, and/or other similar personality rights; however, to 219 | the extent possible, the Licensor waives and/or agrees not to 220 | assert any such rights held by the Licensor to the limited 221 | extent necessary to allow You to exercise the Licensed 222 | Rights, but not otherwise. 223 | 224 | 2. Patent and trademark rights are not licensed under this 225 | Public License. 226 | 227 | 3. To the extent possible, the Licensor waives any right to 228 | collect royalties from You for the exercise of the Licensed 229 | Rights, whether directly or through a collecting society 230 | under any voluntary or waivable statutory or compulsory 231 | licensing scheme. In all other cases the Licensor expressly 232 | reserves any right to collect such royalties, including when 233 | the Licensed Material is used other than for NonCommercial 234 | purposes. 235 | 236 | 237 | Section 3 -- License Conditions. 238 | 239 | Your exercise of the Licensed Rights is expressly made subject to the 240 | following conditions. 241 | 242 | a. Attribution. 243 | 244 | 1. If You Share the Licensed Material (including in modified 245 | form), You must: 246 | 247 | a. retain the following if it is supplied by the Licensor 248 | with the Licensed Material: 249 | 250 | i. identification of the creator(s) of the Licensed 251 | Material and any others designated to receive 252 | attribution, in any reasonable manner requested by 253 | the Licensor (including by pseudonym if 254 | designated); 255 | 256 | ii. a copyright notice; 257 | 258 | iii. a notice that refers to this Public License; 259 | 260 | iv. a notice that refers to the disclaimer of 261 | warranties; 262 | 263 | v. a URI or hyperlink to the Licensed Material to the 264 | extent reasonably practicable; 265 | 266 | b. indicate if You modified the Licensed Material and 267 | retain an indication of any previous modifications; and 268 | 269 | c. indicate the Licensed Material is licensed under this 270 | Public License, and include the text of, or the URI or 271 | hyperlink to, this Public License. 272 | 273 | 2. You may satisfy the conditions in Section 3(a)(1) in any 274 | reasonable manner based on the medium, means, and context in 275 | which You Share the Licensed Material. For example, it may be 276 | reasonable to satisfy the conditions by providing a URI or 277 | hyperlink to a resource that includes the required 278 | information. 279 | 3. If requested by the Licensor, You must remove any of the 280 | information required by Section 3(a)(1)(A) to the extent 281 | reasonably practicable. 282 | 283 | b. ShareAlike. 284 | 285 | In addition to the conditions in Section 3(a), if You Share 286 | Adapted Material You produce, the following conditions also apply. 287 | 288 | 1. The Adapter's License You apply must be a Creative Commons 289 | license with the same License Elements, this version or 290 | later, or a BY-NC-SA Compatible License. 291 | 292 | 2. You must include the text of, or the URI or hyperlink to, the 293 | Adapter's License You apply. You may satisfy this condition 294 | in any reasonable manner based on the medium, means, and 295 | context in which You Share Adapted Material. 296 | 297 | 3. You may not offer or impose any additional or different terms 298 | or conditions on, or apply any Effective Technological 299 | Measures to, Adapted Material that restrict exercise of the 300 | rights granted under the Adapter's License You apply. 301 | 302 | 303 | Section 4 -- Sui Generis Database Rights. 304 | 305 | Where the Licensed Rights include Sui Generis Database Rights that 306 | apply to Your use of the Licensed Material: 307 | 308 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 309 | to extract, reuse, reproduce, and Share all or a substantial 310 | portion of the contents of the database for NonCommercial purposes 311 | only; 312 | 313 | b. if You include all or a substantial portion of the database 314 | contents in a database in which You have Sui Generis Database 315 | Rights, then the database in which You have Sui Generis Database 316 | Rights (but not its individual contents) is Adapted Material, 317 | including for purposes of Section 3(b); and 318 | 319 | c. You must comply with the conditions in Section 3(a) if You Share 320 | all or a substantial portion of the contents of the database. 321 | 322 | For the avoidance of doubt, this Section 4 supplements and does not 323 | replace Your obligations under this Public License where the Licensed 324 | Rights include other Copyright and Similar Rights. 325 | 326 | 327 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 328 | 329 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 330 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 331 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 332 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 333 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 334 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 335 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 336 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 337 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 338 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 339 | 340 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 341 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 342 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 343 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 344 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 345 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 346 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 347 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 348 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 349 | 350 | c. The disclaimer of warranties and limitation of liability provided 351 | above shall be interpreted in a manner that, to the extent 352 | possible, most closely approximates an absolute disclaimer and 353 | waiver of all liability. 354 | 355 | 356 | Section 6 -- Term and Termination. 357 | 358 | a. This Public License applies for the term of the Copyright and 359 | Similar Rights licensed here. However, if You fail to comply with 360 | this Public License, then Your rights under this Public License 361 | terminate automatically. 362 | 363 | b. Where Your right to use the Licensed Material has terminated under 364 | Section 6(a), it reinstates: 365 | 366 | 1. automatically as of the date the violation is cured, provided 367 | it is cured within 30 days of Your discovery of the 368 | violation; or 369 | 370 | 2. upon express reinstatement by the Licensor. 371 | 372 | For the avoidance of doubt, this Section 6(b) does not affect any 373 | right the Licensor may have to seek remedies for Your violations 374 | of this Public License. 375 | 376 | c. For the avoidance of doubt, the Licensor may also offer the 377 | Licensed Material under separate terms or conditions or stop 378 | distributing the Licensed Material at any time; however, doing so 379 | will not terminate this Public License. 380 | 381 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 382 | License. 383 | 384 | 385 | Section 7 -- Other Terms and Conditions. 386 | 387 | a. The Licensor shall not be bound by any additional or different 388 | terms or conditions communicated by You unless expressly agreed. 389 | 390 | b. Any arrangements, understandings, or agreements regarding the 391 | Licensed Material not stated herein are separate from and 392 | independent of the terms and conditions of this Public License. 393 | 394 | 395 | Section 8 -- Interpretation. 396 | 397 | a. For the avoidance of doubt, this Public License does not, and 398 | shall not be interpreted to, reduce, limit, restrict, or impose 399 | conditions on any use of the Licensed Material that could lawfully 400 | be made without permission under this Public License. 401 | 402 | b. To the extent possible, if any provision of this Public License is 403 | deemed unenforceable, it shall be automatically reformed to the 404 | minimum extent necessary to make it enforceable. If the provision 405 | cannot be reformed, it shall be severed from this Public License 406 | without affecting the enforceability of the remaining terms and 407 | conditions. 408 | 409 | c. No term or condition of this Public License will be waived and no 410 | failure to comply consented to unless expressly agreed to by the 411 | Licensor. 412 | 413 | d. Nothing in this Public License constitutes or may be interpreted 414 | as a limitation upon, or waiver of, any privileges and immunities 415 | that apply to the Licensor or You, including from the legal 416 | processes of any jurisdiction or authority. 417 | 418 | ======================================================================= 419 | 420 | Creative Commons is not a party to its public licenses. 421 | Notwithstanding, Creative Commons may elect to apply one of its public 422 | licenses to material it publishes and in those instances will be 423 | considered the "Licensor." Except for the limited purpose of indicating 424 | that material is shared under a Creative Commons public license or as 425 | otherwise permitted by the Creative Commons policies published at 426 | creativecommons.org/policies, Creative Commons does not authorize the 427 | use of the trademark "Creative Commons" or any other trademark or logo 428 | of Creative Commons without its prior written consent including, 429 | without limitation, in connection with any unauthorized modifications 430 | to any of its public licenses or any other arrangements, 431 | understandings, or agreements concerning use of licensed material. For 432 | the avoidance of doubt, this paragraph does not form part of the public 433 | licenses. 434 | 435 | Creative Commons may be contacted at creativecommons.org. 436 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | # Lecture scripts and slides 2 | :livebase: http://static.olivergierke.de/lectures 3 | 4 | image:https://travis-ci.org/olivergierke/lectures.svg?branch=master["Build Status", link="https://travis-ci.org/olivergierke/lectures"] 5 | 6 | This repository contains scripts, slide decks and some sample code for lectures I held. The scripts are written in Asciidoc and rendered using http://asciidoctor.org[Asciidoctor]. Comments and suggestions for improvements are highly welcome. Feel free to open issues or submit a pull request. 7 | 8 | ## Software engineering labs 9 | 10 | * `spring-web-apps` - link:{livebase}/spring-webapps[State-of-the art web applications with Java and Spring] 11 | 12 | ## Software engineering 13 | 14 | * `java-tooling` - link:{livebase}/java-tooling[Java fundamentals and tooling] 15 | * `collaborative-sd` - link:{livebase}/collaborative-sd[Fundamentals of Collaborative Software Development] -------------------------------------------------------------------------------- /collaborative-sd/images/asciidoc-readme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/collaborative-sd/images/asciidoc-readme.png -------------------------------------------------------------------------------- /collaborative-sd/images/git-branches.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/collaborative-sd/images/git-branches.png -------------------------------------------------------------------------------- /collaborative-sd/images/git-changes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/collaborative-sd/images/git-changes.png -------------------------------------------------------------------------------- /collaborative-sd/images/github-commits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/collaborative-sd/images/github-commits.png -------------------------------------------------------------------------------- /collaborative-sd/images/github-issues.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/collaborative-sd/images/github-issues.png -------------------------------------------------------------------------------- /collaborative-sd/images/github-labels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/collaborative-sd/images/github-labels.png -------------------------------------------------------------------------------- /collaborative-sd/images/github-pr-changes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/collaborative-sd/images/github-pr-changes.png -------------------------------------------------------------------------------- /collaborative-sd/images/github-pr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/collaborative-sd/images/github-pr.png -------------------------------------------------------------------------------- /collaborative-sd/images/github-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/collaborative-sd/images/github-project.png -------------------------------------------------------------------------------- /collaborative-sd/images/github-resolved-issues.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/collaborative-sd/images/github-resolved-issues.png -------------------------------------------------------------------------------- /collaborative-sd/images/graphics.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/collaborative-sd/images/graphics.key -------------------------------------------------------------------------------- /collaborative-sd/images/travis-build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/collaborative-sd/images/travis-build.png -------------------------------------------------------------------------------- /collaborative-sd/images/travis-history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/collaborative-sd/images/travis-history.png -------------------------------------------------------------------------------- /collaborative-sd/index.adoc: -------------------------------------------------------------------------------- 1 | # Fundamentals of Collaborative Software Development 2 | Oliver Drotbohm 3 | :revdate: {docdatetime} 4 | :revremark: This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 5 | :numbered: 6 | :experimental: 7 | :source-highlighter: prettify 8 | :sectids!: 9 | :sectanchors: true 10 | :icons: font 11 | :toc: left 12 | :livebase: http://static.odrotbohm.de/lectures 13 | :imagesdir: images/ 14 | 15 | image:https://travis-ci.org/odrotbohm/lectures.svg?branch=master["Build Status", link="https://travis-ci.org/odrotbohm/lectures"] 16 | 17 | :numbered!: 18 | [preface] 19 | [[intro]] 20 | == Introduction 21 | 22 | This script will provide an introduction to team collaboration in software development and give a short overview over the involved tools and practices. 23 | 24 | NOTE: Due to the restricted amount of time available we limit ourselves to the bare minimum of information required to get up to speed for simple projects. 25 | Some of the shown practices here have some drawbacks so that more advanced software projects usually augment and modify them with additional practices. 26 | 27 | [quote, Wikipedia] 28 | A team comprises a group of people or other animals linked in a *common purpose*. Human teams are especially appropriate for conducting tasks that are high in complexity and have many *interdependent subtasks*.footnote:team[Team - http://en.wikipedia.org/wiki/Team[Wikipedia]] 29 | 30 | When developing software in a team some very fundamental challenges arise: 31 | 32 | - *How to share code?* -- A software project usually involves building one or more systems that are built from one or more codebases. 33 | In a team the individual members will have to interact and modify it. 34 | To keep track of the individual changes a so called Version Control Systemfootnote:vcs[Revision Control - http://en.wikipedia.org/wiki/Revision_control[Wikipedia]] is used. We'll cover this in <>. 35 | 36 | - *How to share tasks?* -- The development of a software system is usually split up into individual tasks: implementing a feature, fixing a bug etc. 37 | These tasks are usually kept in an issue tracker that allows to capture the requirements and track progress. 38 | 39 | - *How to connect the former with the latter?* -- Very often, tasks are directly related and even connected to changes in the codebase by some means. <> discusses how to capture tasks and connect them with changes. 40 | 41 | - *Documentation?* -- Software projects require documentation to make sure decisions made during the development process can be understood. We're going to discuss a very pragmatic approach for this in <>. 42 | 43 | [[objectives]] 44 | == Objectives 45 | 46 | After working through this script you should've done -- or at least be able to do the following things: 47 | 48 | * Install Git and a Git UI client on your development machine. 49 | * Create a Git managed project on your local hard drive. 50 | * Create a Github account and fork the Guestbook sample project. 51 | * Clone your Guestbook sample repository to your local machine. 52 | * Setup your Guestbook fork for CI builds with Travis. 53 | 54 | :numbered: 55 | [[version-control]] 56 | == Version Control Systems 57 | 58 | Version Control Systemsfootnote:vcs[] (VCS) allow tracking changes to content of arbitrary type. 59 | They're usually used to track source code and related artifacts. 60 | VCS allow keeping track of who changed what and why by introducing the notion of a commit, a set of changes to the repository attributed to an author and a textual description of the change. 61 | The most prominent VCS these days is Gitfootnote:git[Git - https://git-scm.com/[Website]], but other systems like Mercurial, SVN, CVS, ClearCase or Perforce might cross your paths. 62 | 63 | [[version-control.git]] 64 | === Git 65 | 66 | Git is a distributed version control system brought to life by Linus Torvalds. 67 | In Git commits are identified by a SHA-1 and refer back to their ancestor commits. 68 | 69 | .Git changes 70 | image::git-changes.png[] 71 | 72 | https://git-scm.com[Git] can be installed by downloading the binaries from the project website or like this: 73 | 74 | - Mac OSX (https://brew.sh[Homebew]): `brew install git`. 75 | - Linux: `apt-get install -y git` or your preferred package manager. 76 | - Windows (and any other): Download from the http://git-scm.com/downloads[website]. 77 | 78 | [[version-control.git.quickstart]] 79 | ==== Quickstart 80 | 81 | [source, bash] 82 | ---- 83 | $ mkdir git-sample && cd git-sample<1> 84 | $ git init <2> 85 | 86 | Initialized empty Git repository in /…/.git/ 87 | 88 | $ touch index.adoc <3> 89 | $ … <4> 90 | $ git add . <5> 91 | $ git commit -m "Initial commit." <6> 92 | 93 | [master (root-commit) 7cc7a34] Initial commit. 94 | 1 file changed, 1 insertion(+) 95 | create mode 100644 index.adoc 96 | 97 | $ git log <7> 98 | 99 | commit 7cc7a34f45db1f534a2b90c359429b52ea8e4c94 100 | Author: Oliver Gierke 101 | Date: Wed Jun 3 13:57:07 2015 +0200 102 | 103 | Initial commit. 104 | ---- 105 | <1> Create directory and move into it. 106 | <2> Initializes a Git repository locally. 107 | <3> Creates a file `index.adoc` in the current directory. 108 | <4> Edit the file. 109 | <5> Add all files to the staging area (what is about to be committed). 110 | <6> Commit the changes. 111 | <7> Show the changes in the repository. 112 | 113 | At this point we have made and commited changes to the local repository. 114 | 115 | [[version-control.git.branches]] 116 | ==== Branches 117 | Chains of commits form so called branches. Branches are created for a variety of purposes: 118 | 119 | - *Feature branches* -- these rather short lived branches are created temporarily to isolate independently ongoing work from one another. They allow to control the point of integration of distinct development streams. As merging (see <>) them back together becomes more complicated the more they diverge from each other, care has to be taken to regularly rebase them and keeping the features small. 120 | - *Maintenance branches* -- these rather long lived branches are used to separate pure maintenance work from ongoing development that might introduce new features. Maintenance branches are used in Software Configuration Managementfootnote:scm[Software Configuration Management -- http://en.wikipedia.org/wiki/Software_configuration_management[Wikipedia]] (SCM) to manage the release and maintenance of different versions of a piece of software. 121 | 122 | [[version-control.git.branches-screenshot]] 123 | .The commit history of http://www.st.inf.tu-dresden.de/SalesPoint[Salespoint] in GitX 124 | image::git-branches.png[] 125 | 126 | The screenshot above shows the commit history of the http://www.st.inf.tu-dresden.de/SalesPoint[Salespoint] library in the MacOS Git UI client GitX (read more on Git UI tools in <>). 127 | Each row in the main view represents a single commit: its SHA-1 hash, the commit message summary, the author as well as the date of the commit. 128 | The colored labels represent branches (orange: currently checked out branch, green: other local branches, blue: remote branches (see <> for details)) or tags (yellow). 129 | A tag is a reference to a particular state of the repository and usually used to indicate the commit that has been used to craft a release of a piece of software. 130 | 131 | As you can see, commit `dc8a944` has two subsequent commits `feb119c` and `1c8ed65`. `dc8a944` is the point where the `6.1.x` branch was branched of the main development line. 132 | It's not by accident that this is also the commit that's tagged with `6.1.0.RELEASE` as it marks the starting point of the maintenance branch, which has seen a bugfix release in commit `96105b8`. 133 | 134 | [[version-control.git.remote-repositories]] 135 | ==== Interacting with a remote repository 136 | 137 | Git is a distributed VCS, which means that clones of a repository can and will exist in different locations. 138 | The most rudimentary setup is a canonical remote repository usually hosted by a Git server as well as local repositories on the individual developer's machines. 139 | This creates the challenge to synchronize sets of commits between individual repositories. 140 | 141 | [source, bash] 142 | ---- 143 | $ git remote add origin https://… <1> 144 | $ git push origin master <2> 145 | $ git pull origin master <3> 146 | ---- 147 | <1> Adds a remote repository reference named `origin` to the local one. 148 | <2> Pushes the local commits of the current branch to the remote branch named `master` in the repository named `origin`. 149 | <3> Pulls commits made to the `master` in the remote repository into the current branch. 150 | 151 | [[version-control.git.tools]] 152 | ==== Tools 153 | 154 | - https://git-scm.com/downloads/guis[GUI clients overview] 155 | - GitHub for http://mac.github.com[Mac] / http://windows.github.com/[Windows] 156 | - https://www.sourcetreeapp.com/[SourceTree] 157 | - https://www.eclipse.org/egit/[EGit] 158 | 159 | [[version-control.git.tutorials]] 160 | ==== Tutorials 161 | 162 | - http://rogerdudler.github.io/git-guide/index.html[Git - The Simple Guide] - Roger Dudler 163 | - http://www.vogella.com/tutorials/Git/article.html[Distributed Version Control with Git] - Lars Vogel (esp. chapters 1, 2). 164 | - http://www.vogella.com/tutorials/EclipseGit/article.html[Git Version Control with Eclipse] - Lars Vogel 165 | 166 | [[github]] 167 | == GitHub 168 | 169 | [quote, Github] 170 | Build software better, together. 171 | 172 | GitHub is a Software As A Servicefootnote:saas[Software As A Service - http://en.wikipedia.org/wiki/Software_as_a_service[Wikipedia]] (SAAS) platform for collaborative software development. 173 | It allows to host Git repositories, track issues and host documentation and release binaries. 174 | It provides free service for public repositories 175 | 176 | .GitHub project 177 | image::github-project.png[] 178 | 179 | .GitHub commits 180 | image::github-commits.png[] 181 | 182 | [[github.issues]] 183 | === Tracking issues 184 | 185 | .GitHub issues 186 | image::github-issues.png[] 187 | 188 | .GitHub labels 189 | image::github-labels.png[] 190 | 191 | .GitHub resolved issues 192 | image::github-resolved-issues.png[] 193 | 194 | [[github.pull-requests]] 195 | === Pull requests 196 | 197 | Pull requests are GitHub's way of implementing code reviews: 198 | 199 | [quote, Wikipedia] 200 | Code review is systematic examination (often known as peer review) of computer source code. It is intended to find and fix mistakes overlooked in the initial development phase, improving both the overall quality of software and the developers' skills.footnote:code-review[Code review - http://en.wikipedia.org/wiki/Code_review[Wikipedia]] 201 | 202 | A pull request is a post-commit variant of a code review which means the original developer pushes the code to be reviewed into branch in a remote repository. 203 | The GitHub UI then allows to create a pull request which formally expresses the desire of the contributor to get a set of changes integrated with the project. 204 | The team then reviews the changes, comments on them recommends further changes. 205 | Subsequent commits to the branch add up on the changes. 206 | Once the team reaches consensus about the scope and quality of the changes they are merged back into project by one of the team members. 207 | 208 | .A pull request 209 | image::github-pr.png[] 210 | 211 | .The changes contained in a pull request 212 | image::github-pr-changes.png[] 213 | 214 | [[github.merges]] 215 | === Merges 216 | 217 | Mergesfootnote:merge[Merge - http://en.wikipedia.org/wiki/Merge_(revision_control)[Wikipedia]] are a crucial task in working with code in distributed teams. 218 | If changes that already have been merged overlap with changes to be merged the risk of so called merge conflicts arise. 219 | These usually have to be resolved manually by inspecting the conflicting changes and consolidating using a so called diff or merge tool. 220 | 221 | Generally speaking it's preferable to organize work -- and thus the code -- into parts that can be changed independently. Another option is to try to estimate the reach of changes for particular tasks and schedule them to be worked on subsequently. 222 | 223 | ==== General recommendations 224 | 225 | - *Create issues per task* -- to be able to keep track of which changes relate to which task it's best to create tickets for each of them. This allows you to refer to these tasks using the ticket identifiers. 226 | - *Make sure changes in a commit / PR only target one task* -- Keeping track of which changes were made for which reason is significantly harder if a commit contains changes that relate to multiple tickets. Try to focus on changes for a dedicated task and commit early and often. 227 | - *Create a feature branch per issue* -- To be able to switch tasks and keep the commit history of the master branch clean create feature branches that contain commits related to a particular ticket. 228 | - *Keep feature branches small and short-lived* -- make sure, feature branches live for very limited time and don't contain too many changes as they increase the probability for merge conflicts to occur. 229 | If you find yourself with huge changes in a feature branch, you might wanna rethink the granularity of tasks. 230 | Feature branches shouldn't live for more than a couple of days. 231 | - *Good commit messages* -- the only way for your colleagues to understand the reasoning behind a commit is reading the commit messages. 232 | Thus a "changed something" isn't incredibly helpful. 233 | Describe what you changed and -- even more importantly -- why you changed what on a high level. 234 | - *Refer to tickets from the code and commit message* -- GitHub detects ticket references (i.e. `\#4711`) and links them from the tickets. 235 | It even supports keywords like `fixes` to automatically resolve a ticket when pushing the commit. 236 | An example of this can be seen in the lower third of the screenshot in <>. 237 | 238 | [[ci]] 239 | == Continuous integration 240 | 241 | [quote, Martin Fowler] 242 | Continuous Integration (CI) is a software development practice where members of a team integrate their work frequently, usually each person integrates at least daily - leading to multiple integrations per day.footnote:ci[Martin Fowler – http://martinfowler.com/articles/continuousIntegration.html[Continuous Integration]] 243 | 244 | Continuous integration is the practice of building a software system on a regular basis and thus require an link:{livebase}/java-tooling#build[automated build]. 245 | 246 | [[ci.travis]] 247 | === Travis 248 | 249 | Travisfootnote:travis[Travis CI - https://travis-ci.org[Website]] is a CI service for free to use with public GitHub repository that allows a build per commit. 250 | 251 | .Travis build 252 | image::travis-build.png[] 253 | 254 | .Travis build history 255 | image::travis-history.png[] 256 | 257 | [[ci.travis.configuration]] 258 | ==== Configuration 259 | 260 | Continuous integration requires the definition of which tasks to actually execute for a build. Travis inspects a YAML file named `.travis.yml` in the project root to pick up customizations to the build. 261 | 262 | ==== 263 | .Travis configuration in Guestbook 264 | [source] 265 | ---- 266 | language: java <1> 267 | jdk: 268 | - oraclejdk8 <2> 269 | ---- 270 | <1> Defines the project to require a JVM to run and triggers default build execution for Java projects. 271 | <2> Defines the project to be build with Java 8. 272 | ==== 273 | 274 | [[documentation]] 275 | == Documentation 276 | 277 | Software systems usually ship with documentation of various kinds: 278 | 279 | - *End-user documentation* -- documents how to interact and work with the running systems and describes it from an end-user's point of view. 280 | - *Developer documentation* -- documents architecture and design decisions made during the course of development. It mostly targets (future) developers of the system. 281 | 282 | Developer documentation itself usually consists of a variety of documentation formats, too: 283 | 284 | - *Source code comments* -- in the Java space usually JavaDoc. This kind of documentation is close to the code and turned into externally accessible HTML during the build. 285 | - *Readme* -- Fundamental, human readable instructions to build and run the software. Located at the repository root and automatically rendered by GitHub. 286 | - *Reference documentation* -- Higher level documentation about design and architecture decisions. Can be built with the project using the build system. Alternatively -- when working with GitHub -- the wiki can be used. 287 | 288 | The latter two beg the question of which technical format to use for writing. Selecting a suitable format should be driven by the following factors: 289 | 290 | - *Distraction-free writing* -- the format should be easily editable, don't make you think but at the same time support all the necessary style elements that might be needed. 291 | - *Comprehensive tooling for processing* -- the format should be easily transformable into distribution formats consumable by mere mortals (single-sourcing). 292 | 293 | [[documentation.asciidoctor]] 294 | === Asciidoc(tor) 295 | 296 | [quote, Asciidoc] 297 | AsciiDoc is a text document format for writing notes, documentation, articles, books, ebooks, slideshows, web pages, man pages and blogs. AsciiDoc files can be translated to many formats including HTML, PDF, EPUB, man page.footnote:asciidoc[Asciidoc - http://www.methods.co.nz/asciidoc/[Website]] 298 | 299 | Asciidoc shines because of its simple syntax but more complete set of structural elements available. 300 | Markdown is a decent choice for very simple documents, too, but lacks important structural elements like tables, footnotes, etc. 301 | 302 | As Asciidoc is a simple text format, documents can be edited using any text editor. A lot of the popular ones these days (Sublime Text, Atom etc.) even have dedicated support for syntax highlighting etc. 303 | 304 | [quote, Asciidoctor] 305 | A fast text processor & publishing toolchain for converting AsciiDoc to HTML5, DocBook & more.footnote:asciidoctor[Asciidoctor - http://asciidoctor.org/[Website]] 306 | 307 | Asciidoctor is an open source implementation of Asciidoc and provides tools and build system integration to build human-readable versions of the documentation. 308 | 309 | [[documentation.asciidoctor.render]] 310 | ==== How to render Asciidoc files? 311 | 312 | A very easy way to preview Asciidoc files is the Asciidoctor.js Live Previewfootnote:asciidoctor-chrome[Asciidoctor.js Live Preview - https://chrome.google.com/webstore/detail/asciidoctorjs-live-previe/iaalpfgpbocpdfblpnhhgllgbdbchmia?hl=en[Google Chrome Webstore]]. Simply drag an Asciidoc file into the browser and the plugin will render an HTML preview of the file. 313 | 314 | For a build on the command line, install Asciidoctor as described in its reference documentationfootnote:asciidoctor-install[Asciidoctor - http://asciidoctor.org/docs/install-toolchain/[Installation instructions]]. 315 | 316 | GitHub supports Asciidoc out of the box and automatically renders Asciidoc files when previewing them. E.g. the readme of the repository hosting this lecture is written in Asciidoc: 317 | 318 | .Asciidoc files rendered by github 319 | image::asciidoc-readme.png[] 320 | 321 | Last but not least documentation can be rendered during the project build as plugins for Java build systems (Maven, Gradle) exist. 322 | 323 | [[recommendations]] 324 | == Recommendations from the Twittersphere 325 | 326 | See https://twitter.com/odrotbohm/status/606050631653183488[this conversation] for all replies. 327 | 328 | [quote, Oliver Gierke – @odrotbohm] 329 | If you had to teach newbies 2 or three fundamental technical things about collaborative software development, what would that be? 330 | 331 | [quote, Daniel Barth – @devkiela] 332 | Technically I would say DVCS/social coding and reproducible builds/dependency mngmt. And TDD of course to not break things. 333 | 334 | [quote, Gerrit Meier - @meistermeier] 335 | Commit (and push) often / keep changes from master in sync to avoid the merge day / learn command line first. 336 | 337 | [quote, Jochen Mader - @codepitbull] 338 | If stuck on a problem for longer than 30 minutes ASK!!!! 339 | 340 | [quote, Markus Tacker - @coderbyheart] 341 | Ask until you really understand the problem. 342 | Learn to give constructive feedback. 343 | You don't own the code but the team does. 344 | -------------------------------------------------------------------------------- /ddd-and-spring/images/bounded-context.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/ddd-and-spring/images/bounded-context.png -------------------------------------------------------------------------------- /ddd-and-spring/images/bounded-context1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/ddd-and-spring/images/bounded-context1.png -------------------------------------------------------------------------------- /ddd-and-spring/images/ddd-building-blocks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/ddd-and-spring/images/ddd-building-blocks.png -------------------------------------------------------------------------------- /ddd-and-spring/images/ddd-quickly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/ddd-and-spring/images/ddd-quickly.png -------------------------------------------------------------------------------- /ddd-and-spring/images/model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/ddd-and-spring/images/model.png -------------------------------------------------------------------------------- /ddd-and-spring/index.adoc: -------------------------------------------------------------------------------- 1 | = Domain-Driven Design and Spring 2 | Oliver Gierke 3 | :revdate: {docdatetime} 4 | :revremark: This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 5 | :numbered: 6 | :experimental: 7 | :source-highlighter: prettify 8 | :sectids!: 9 | :sectanchors: true 10 | :icons: font 11 | :toc: left 12 | :toclevels: 3 13 | :livebase: http://static.olivergierke.de/lectures 14 | :imagesdir: images 15 | 16 | [[intro]] 17 | == Introduction 18 | 19 | This script is supposed to give a brief overview about the fundamental building blocks and concepts of Domain-Driven Design focussing on their application to Java (and mostly Spring) based web applications as well as how these building blocks translate into different groups of classes with different traits. 20 | 21 | The second part focuses on the concepts of entities, aggregate roots and repositories and how they map to Spring based Java applications. 22 | 23 | [[ddd]] 24 | == Domain-Driven Design 25 | 26 | <> is a book by Eric Evans and is undoubtedly one of the most important books on software design. A more compact version of the book is available as <> on InfoQ. 27 | 28 | The book's core focus is on the domain a piece of software is supposed to deal with and how that domain actually connects to the software: starting with an emphasis on the domain's ubiquitous language, approaches to distilling a model from that language and eventually turning this into working software reflecting that model. 29 | 30 | While software design patternsfootnote:software-patterns[Software Design Patterns - https://en.wikipedia.org/wiki/Software_design_pattern[Wikipedia]] are usually defined through a technical context and are used to describe and capture relationships of classes, DDD is a more wholistic approach to find a common language between programmers and business and ultimately connect the code to be written to the domain. 31 | In other words, while you might be familiar with more technical patterns already, in business code they're mostly perceived as technical noisefootnote:patterns-as-technical-noise[Patterns as technical noise - https://www.slideshare.net/cyriux/ddd-patterns-that-were-not-in-the-book[Slide deck, slide 54]]. 32 | Also, the original authors of the GoF book have shared incisive insightsfootnote:gof-interview[Design Patterns 15 Years Later: An Interview with Erich Gamma, Richard Helm, and Ralph Johnson, http://www.informit.com/articles/article.aspx?p=1404056[Interview]] about what they'd do differently if the book ever got an update, including which patterns to deemphasize, how to slightly change the organization of the patterns etc. 33 | 34 | [[ddd.building-blocks]] 35 | === Building blocks 36 | 37 | The building blocks define common terms that are used to describe model elements and assign certain traits to them. Let's have a look at the most fundamental ones here. 38 | 39 | image::ddd-building-blocks.png[] 40 | 41 | [[ddd.building-blocks.bounded-context]] 42 | ==== Bounded context 43 | 44 | When modeling a domain any attempt to model it as a whole is doomed to fail as the sheer number of stakeholders and their different views on the domain might be very different and trying to craft a single, unique model to satisfy all of the needs might be either completely impossible or the source of great complexity in the best case. 45 | Let's have a look at a sample sketch that outlines identified concepts in a a Point of Sales (POS) domain. 46 | 47 | .Model elements in a point of sales application 48 | image::model.png[] 49 | 50 | As you can see we already grouped the model elements slightly to differentiate from more remote ones. Looking at the core concepts of customer and order here we can still identify different contexts in which the model elements just discovered might play a role in. 51 | 52 | .Identified bounded contexts 53 | image::bounded-context.png[] 54 | 55 | Here we identified core high-level parts of the system that might all deal with the concept of a customer or an order but are usually interested in different aspects of them. 56 | The accounting context is usually interested in billing information of a customer and different payment options while the shipping context's sole interest is usually shipping addresses and tracking an order. 57 | The order context might know about a product through its line items but actually only refers to what is essentially maintained by the catalog. 58 | 59 | .Model elements in bounded contexts 60 | image::bounded-context1.png[] 61 | 62 | This boils down to model elements which might appear as a singular concept at first glance needing to be reflected in different way in different aspects of the system. 63 | Modern software architecture even sometimes takes this a step further and even aligns system boundaries with such a bounded context, actively accepts redundancy and eventual consistency to reduce the complexity of the individual systems, fosters resilience and increases development productivity.footnote:microservices[Microservices - https://en.wikipedia.org/wiki/Microservices[Wikipedia]]. 64 | 65 | [[ddd.building-blocks.value-objects]] 66 | ==== Value objects 67 | 68 | Represent a concept from the domain that -- as the name suggests -- is considered a value. 69 | In terms of classes that means that individual instances don't have identity, no life cycle. 70 | A value object needs to be immutable to ensure integrity of the instances as they can be shared amongst different consumers. 71 | In the Java language, an instance of `String` is a good example of these traits, although it's not a good example for a domain concept as it's rather generic. 72 | A credit card number, an email address are great candidates to be modeled as value objects. 73 | Value objects are usually part of other model elements like entities or services. 74 | Implementing model elements as value objects also has a great impact on legibility and comprehensibility of the code base as Dan Bergh Johnsson demonstrates in his talk <>. 75 | 76 | Value objects are often undervalued when it comes to the translation of model into code as the implementation of a value object in plain Java is quite cumbersome due to the need to implement accessors,`equals(…)` and `hashCode()`. 77 | This can be countered with a tiny annotation processor called Lombokfootnote:lombok[Project Lombok - https://projectlombok.org[Project website]]. 78 | 79 | [[ddd.building-blocks.entites]] 80 | ==== Entities 81 | 82 | In comparison to value objects, an entity's core trait is it's identity. 83 | Two customers named Michael Müller might not constitue the very same instance, so that usually a dedicated property is introduced to capture the identity. 84 | Another core trait of entities is that they're usually subject to a certain lifecycle within the problem domain. 85 | They get created, they undergo certain state changes usually driven by domain events and might reach an end state (i.e. might be deleted, although this doesn't necessary mean that the information is removed from the system). 86 | Entities usually relate to other entities and contain properties that are value objects or primitives (the former preferred). 87 | 88 | [[ddd.building-blocks.entites-vs-value-objects]] 89 | ===== Entities VS. Value objects 90 | 91 | It's not always clear whether to model a domain concept as value object or entity. 92 | In fact the concept of an address can -- depending on the context -- even be modeled as both within the same application. 93 | While the address of a store might be a value object might just be part of the domain concept store, it might as well be an entity in the context of modeling a customer as shipping and billing addresses might have to be created, edited, deleted etc. 94 | 95 | [[ddd.building-blocks.aggregates]] 96 | ==== Aggregate roots 97 | 98 | Within the set of entities of a system, some usually play a special role in their relationship to others. 99 | Consider an order consisting of line items. The order might expose a total which is calculated from the prices of the individual line items. 100 | It might only be in a valid state if it is more than a certain minimum total. 101 | The root entities of such a construct are usually conceptually elevated to a so called aggregate root and thus create certain implications: 102 | 103 | - The aggregate root is responsible to assert invariants on the entire aggregate. State changes on the aggregate always result in a valid result state or trigger an exception. 104 | - To fulfil this responsibility, only aggregate roots can be accessed by repositories (see <>). State changes involving non-root entities need to be applied by obtaining the aggregate root and triggering it on the root. 105 | - As an aggregate forms a natural consistency boundaries, references to other aggregates should be implemented in a by-id way. 106 | 107 | [[ddd.building-blocks.repositories]] 108 | ==== Repositories 109 | 110 | Conceptually a repository simulates a collection of aggregate roots and allows accessing subsets or individual items. They're usually backed by some kind of persistence mechanism but shouldn't expose it to client code. Repositories refer to entities, not the other way round. 111 | 112 | ==== Domain services 113 | 114 | Domain services implement functionality that cannot uniquely be assigned to an entity or value object or need to orchestrate logic between them and repositories. Business logic should be implemented in entities and value objects as much as possible as it can be tested more easily within them. 115 | 116 | [[ddd.spring]] 117 | === Domain-Driven Design in a Spring application 118 | 119 | The mapping of a domain concept to a DDD concept has quite a few important implications for the way these concepts are reflected in the code. To work effectively with Spring based Java applications, it's important to distinguish between that category of newables and injectables. 120 | 121 | As the names suggest, the differentiating line is drawn between the ways a developer gets hold of an instance of a class for that particular model element. 122 | A newable can be simply instantiated using the `new` operator, although even that should be limited to as few places as possible. The factory pattern can help here, too. 123 | Entities and value objects are newables. 124 | An injectable is usually a Spring component, which means that the latter controls its lifecycle, creates instances and destroys them. This allows the container to equip the service instance with technical services like transactions or security. 125 | Clients obtain instances by using dependency injection (hence the name injectable). 126 | Repositories and services are injectables. 127 | 128 | This distinction between these two groups of classes naturally defines a preferred dependency direction from injectables to newables. Generally speaking 129 | 130 | - _Value object_ - JPA `@Embeddable` + corresponding `equals(…)` and `hashCode()` (Lombok's `@Value` helps here). Can depend on other value objects and entities. 131 | - _Entity_ - JPA `@Entity` + corresponding `equals(…)` and `hashCode()` implementations. Can depend on other entities and value objects. 132 | - _Repository_ - Spring component, usually a Spring Data repository interface. Can depend on entities and value objects, are centered around entities that are aggregate roots. 133 | - _Domain services_ - Usually a Spring component, a class annotated with `@Component` or a stereotype annotation. Can also be modeled as newables in some cases in case they don't require technical services to be applied (e.g. security, transactions). 134 | 135 | Not all classes of a Spring application can be assigned to these DDD categories. These other classes can usually be grouped into the following, more technical ones: 136 | 137 | - _Application configuration_ - Classes to configure components. 138 | - _Technical adapters_ - Business logic implemented in a Spring application is usually exposed to clients through some remoting technology. In a web application these technologies are HTTP, HTML and JavaScript. With Spring MVC, controller classes serve the purpose of translating the concepts of the remoting technology (e.g. the notion of a request, request parameters, a payload, a response etc.) into the domain concepts and invoke services with value objects and entities. 139 | 140 | [[further-reading]] 141 | == Further reading 142 | 143 | 1. Check out the Guestbookfootnote:guestbook[Guestbook - https://github.com/st-tu-dresden/guestbook[Sample application on GitHub]] and Videoshopfootnote:videoshop[Videoshop - https://github.com/st-tu-dresden/videoshop[Sample application on GitHub]] and make sure you understand which classes implement which DDD concepts. 144 | 2. Make sure you understand how the different traits of these concepts (identity for entities, immutability for value objects, dependency injection for services) are implemented. 145 | 146 | :numbered!: 147 | == Appendix 148 | 149 | [appendix] 150 | === Bibliography 151 | 152 | [bibliography] 153 | - [[[ddd-book]]] - http://www.amazon.de/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215[Eric Evans -- Domain-Driven Design: Tackling Complexity in the Heart of Software]- Addison Wesley. 2003. 154 | - [[[ddd-quickly]]] - http://www.infoq.com/minibooks/domain-driven-design-quickly[Abel Avram, Floyd Marinescu -- Domain-Driven Design Quickly]. InfoQ. 2006. 155 | - [[[power-of-value-objects]]] - http://www.infoq.com/presentations/Value-Objects-Dan-Bergh-Johnsson[Dan Bergh Johnsson -- Power Use of Value Objects in DDD]. InfoQ. 2009. 156 | 157 | [appendix] 158 | === License 159 | image::https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png[link="http://creativecommons.org/licenses/by-nc-sa/4.0/"] 160 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ./render.sh 3 | 4 | read -p "User: " user 5 | read -s -p "Password: " password 6 | 7 | # Uploade individual lectures 8 | for f in $(find . -name 'index.adoc' -exec dirname {} \; | grep -v "./index"); do 9 | 10 | find ${f} -type f \( -name "*.html" -o -name "*.css" -o -name "*.js" -o -name "*.png" -o -name "*.gif" -o -name "*.pdf" \) \ 11 | -exec echo {} \; \ 12 | -exec curl --ftp-create-dirs -T {} -u $user:$password ftp://olivergierke.de/static/lectures/{} \; 13 | 14 | done; 15 | 16 | # Upload index page 17 | 18 | cd index 19 | 20 | find . -type f \( -name "*.html" -o -name "*.css" -o -name "*.js" -o -name "*.png" -o -name "*.gif" -o -name "*.pdf" \) \ 21 | -exec echo {} \; \ 22 | -exec curl --ftp-create-dirs -T {} -u $user:$password ftp://olivergierke.de/static/lectures/{} \; 23 | -------------------------------------------------------------------------------- /frameworks-and-libraries/images/project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/frameworks-and-libraries/images/project.png -------------------------------------------------------------------------------- /frameworks-and-libraries/images/sts-components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/frameworks-and-libraries/images/sts-components.png -------------------------------------------------------------------------------- /frameworks-and-libraries/images/types.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/frameworks-and-libraries/images/types.png -------------------------------------------------------------------------------- /frameworks-and-libraries/index.adoc: -------------------------------------------------------------------------------- 1 | = Frameworks and Libraries 2 | Oliver Drotbohm 3 | :revdate: {docdatetime} 4 | :revremark: This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 5 | :numbered: 6 | :experimental: 7 | :sectids!: 8 | :sectanchors: true 9 | :icons: font 10 | :toc: left 11 | :toclevels: 3 12 | :livebase: http://static.odrotbohm.de/lectures 13 | :imagesdir: images 14 | 15 | [[intro]] 16 | == Introduction 17 | 18 | This script is supposed to give an overview about the concept of a library and a framework and distinguish one from another. 19 | We're going to look at JUnit as an example of a test framework and Spring as an example of a general purpose application framework. 20 | 21 | [[intro.frameworks-vs-libraries]] 22 | === Frameworks VS. Libraries 23 | 24 | * Library: your coded uses the provided code (e.g. Google Guava, Spring JDBC) 25 | * Framework: the provided code uses your code (e.g. JUnit, Spring Framework) 26 | 27 | [[junit]] 28 | == JUnit -- a test framework 29 | 30 | JUnit is a Java based test framework that -- contrary to what its name implies -- can be used to write both unit tests and integration tests. 31 | Until version 3, it used an inheritance based approach for its user-facing API. 32 | That has unanimously been considered too invasive so that the current framework generation relies on annotations. 33 | 34 | [[junit.unit-vs-integration-tests]] 35 | === Unit- VS. Integration tests 36 | 37 | Software can be tested on multiple levels of granularity: individual components can be tested in isolation, in the context of collaborating components. 38 | The entire system can be tested as a whole. 39 | 40 | The most fundamental approaches to testing are called unit tests. 41 | They rely on the component to be testable with a minimal amount of context available. 42 | It's usually desirable to use a class on its own, potentially replacing required collaborators with test mocks or stubs. 43 | To make sure the effort for that is manageable, this requires the class under test to be used in an isolated way. 44 | 45 | [quote, Sarah Mei - @sarahmei] 46 | ____ 47 | The four primary reasons developers write tests: 48 | 49 | 1. Prevent regressions 50 | 2. Improve design 51 | 3. Enable later refactoring 52 | 4. Document behavior 53 | ____ 54 | 55 | Thus writing unit tests for a component usually drives the design of that class to become more simple. The approach of writing test cases to drive the design of code is called Test Driven Design/Development (TDDfootnote:tdd[Test-Driven Development - https://en.wikipedia.org/wiki/Test-driven_development[Wikipedia]]) 56 | 57 | include::../spring-webapps-old/quickstart.adoc[leveloffset=+1] 58 | 59 | include::../spring-webapps-old/spring.adoc[leveloffset=+1] 60 | 61 | //== Injectables VS Newables 62 | 63 | // [CustomerManagement|register(firstname:String;lastname:String;birthday:LocalDate):Customer;findByLastname(lastname:String):Collection]<-.-[DefaultCustomerManagement], [DefaultCustomerManagement]->[CustomerRepository|save(customer:Customer):Customer;findByLastnameContainingIgnoreCase(lastname:String):Collection], [CustomerRepository]<-.-[JpaCustomerRepository], [CustomerRepository]<-.-[InMemoryCustomerRepository], [Customer|+firstname;+lastname;-birthday|getAge();isAdult()] 64 | == Sample class design 65 | image::types.png[] 66 | 67 | :numbered!: 68 | == Appendix 69 | 70 | [appendix] 71 | === License 72 | image::https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png[link="http://creativecommons.org/licenses/by-nc-sa/4.0/"] 73 | -------------------------------------------------------------------------------- /frameworks-and-libraries/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 4.0.0 5 | 6 | de.olivergierke.lectures 7 | frameworks-and-libraries-parent 8 | 1.0.0-SNAPSHOT 9 | pom 10 | 11 | Frameworks and Libraries - All 12 | 13 | 14 | quickstart 15 | spring 16 | 17 | 18 | -------------------------------------------------------------------------------- /frameworks-and-libraries/quickstart/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 4.0.0 5 | 6 | de.olivergierke.lectures 7 | spring-web-quickstart 8 | 1.0.0-SNAPSHOT 9 | 10 | Spring Web Application Quickstart 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-parent 15 | 1.2.4.RELEASE 16 | 17 | 18 | 19 | 1.8 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-web 29 | 30 | 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-test 36 | test 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /frameworks-and-libraries/quickstart/src/main/java/demo/Application.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package demo; 17 | 18 | import org.springframework.boot.SpringApplication; 19 | import org.springframework.boot.autoconfigure.SpringBootApplication; 20 | 21 | /** 22 | * @author Oliver Gierke 23 | */ 24 | @SpringBootApplication 25 | public class Application { 26 | 27 | public static void main(String... args) { 28 | SpringApplication.run(Application.class, args); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /frameworks-and-libraries/quickstart/src/main/java/demo/WelcomeController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package demo; 17 | 18 | import org.springframework.web.bind.annotation.RequestMapping; 19 | import org.springframework.web.bind.annotation.RequestParam; 20 | import org.springframework.web.bind.annotation.RestController; 21 | 22 | /** 23 | * Sample controller to demonstrate basic web functionality. 24 | * 25 | * @author Oliver Gierke 26 | */ 27 | @RestController 28 | class WelcomeController { 29 | 30 | @RequestMapping("/welcome") 31 | String welcome(@RequestParam(value = "name", defaultValue = "World") String name) { 32 | return "Welcome ".concat(name).concat("!"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /frameworks-and-libraries/quickstart/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/frameworks-and-libraries/quickstart/src/main/resources/application.properties -------------------------------------------------------------------------------- /frameworks-and-libraries/quickstart/src/test/java/demo/ApplicationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package demo; 17 | 18 | import org.junit.Test; 19 | import org.junit.runner.RunWith; 20 | import org.springframework.boot.test.SpringApplicationConfiguration; 21 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 22 | 23 | /** 24 | * Application integration tests. 25 | * 26 | * @author Oliver Gierke 27 | */ 28 | @RunWith(SpringJUnit4ClassRunner.class) 29 | @SpringApplicationConfiguration(classes = Application.class) 30 | public class ApplicationTests { 31 | 32 | @Test 33 | public void bootstrapsApplication() {} 34 | } 35 | -------------------------------------------------------------------------------- /frameworks-and-libraries/spring/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 4.0.0 5 | 6 | de.olivergierke.lectures 7 | frameworks-and-libraries 8 | 1.0.0-SNAPSHOT 9 | 10 | Frameworks and Libraries 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-parent 15 | 1.2.4.RELEASE 16 | 17 | 18 | 19 | 1.8 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-data-jpa 29 | 30 | 31 | 32 | org.hsqldb 33 | hsqldb 34 | 35 | 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-test 41 | test 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /frameworks-and-libraries/spring/src/main/java/example/complete/Application.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example.complete; 17 | 18 | import org.springframework.boot.SpringApplication; 19 | import org.springframework.boot.autoconfigure.SpringBootApplication; 20 | 21 | /** 22 | * @author Oliver Gierke 23 | */ 24 | @SpringBootApplication 25 | public class Application { 26 | 27 | public static void main(String[] args) { 28 | SpringApplication.run(Application.class, args); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /frameworks-and-libraries/spring/src/main/java/example/complete/Customer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example.complete; 17 | 18 | import java.time.LocalDate; 19 | import java.time.Period; 20 | 21 | import javax.persistence.Entity; 22 | import javax.persistence.GeneratedValue; 23 | import javax.persistence.Id; 24 | 25 | import org.springframework.util.Assert; 26 | import org.springframework.util.ObjectUtils; 27 | 28 | /** 29 | * @author Oliver Gierke 30 | */ 31 | @Entity 32 | public class Customer { 33 | 34 | private @GeneratedValue @Id Long id; 35 | private final String firstname, lastname; 36 | private final LocalDate birthday; 37 | 38 | Customer(String firstname, String lastname, LocalDate birthday) { 39 | 40 | Assert.hasText(firstname, "Firstname must not be null or empty!"); 41 | Assert.hasText(lastname, "Lastname must not be null or empty!"); 42 | Assert.isTrue(birthday.isBefore(LocalDate.now()), "Date of birth must be in the past!"); 43 | 44 | this.firstname = firstname; 45 | this.lastname = lastname; 46 | this.birthday = birthday; 47 | } 48 | 49 | Customer() { 50 | this.firstname = null; 51 | this.lastname = null; 52 | this.birthday = null; 53 | } 54 | 55 | /** 56 | * @return the firstname 57 | */ 58 | public String getFirstname() { 59 | return firstname; 60 | } 61 | 62 | /** 63 | * @return the lastname 64 | */ 65 | public String getLastname() { 66 | return lastname; 67 | } 68 | 69 | public int getAge() { 70 | return Period.between(birthday, LocalDate.now()).getYears(); 71 | } 72 | 73 | public boolean isAdult() { 74 | return getAge() >= 18; 75 | } 76 | 77 | /* 78 | * (non-Javadoc) 79 | * @see java.lang.Object#equals(java.lang.Object) 80 | */ 81 | @Override 82 | public boolean equals(Object obj) { 83 | 84 | if (this == obj) { 85 | return true; 86 | } 87 | 88 | if (!(obj instanceof Customer)) { 89 | return false; 90 | } 91 | 92 | Customer that = (Customer) obj; 93 | 94 | return this.id == null ? false : this.id.equals(that.id); 95 | } 96 | 97 | /* 98 | * (non-Javadoc) 99 | * @see java.lang.Object#hashCode() 100 | */ 101 | @Override 102 | public int hashCode() { 103 | return ObjectUtils.nullSafeHashCode(this.id); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /frameworks-and-libraries/spring/src/main/java/example/complete/CustomerManagement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example.complete; 17 | 18 | import java.time.LocalDate; 19 | import java.util.Collection; 20 | 21 | /** 22 | * Service interface to manage {@link Customer}s. 23 | * 24 | * @author Oliver Gierke 25 | */ 26 | public interface CustomerManagement { 27 | 28 | /** 29 | * Registers a {@link Customer} with the given firstname, lastname and birthday in the system. 30 | * 31 | * @param firstname must not be {@literal null} or empty. 32 | * @param lastname must not be {@literal null} or empty. 33 | * @param birthday must not be {@literal null}. 34 | * @return 35 | */ 36 | Customer registerCustomer(String firstname, String lastname, LocalDate birthday); 37 | 38 | /** 39 | * Returns all {@link Customer}s that have a lastname containing the given snippet. 40 | * 41 | * @param part can be {@literal null} or empty. 42 | * @return 43 | */ 44 | Collection findByLastname(String part); 45 | } 46 | -------------------------------------------------------------------------------- /frameworks-and-libraries/spring/src/main/java/example/complete/CustomerRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example.complete; 17 | 18 | import java.util.Collection; 19 | 20 | /** 21 | * Interface to define a repository of {@link Customer}s. 22 | * 23 | * @author Oliver Gierke 24 | */ 25 | public interface CustomerRepository { 26 | 27 | /** 28 | * Saves the given {@link Customer}. 29 | * 30 | * @param customer must not be {@literal null}. 31 | * @return 32 | */ 33 | Customer save(Customer customer); 34 | 35 | /** 36 | * Returns all {@link Customer}s with a lastname containing the given snippet independent of case. 37 | * 38 | * @param lastname can be {@literal null} or empty. 39 | * @return 40 | */ 41 | Collection findByLastnameContainingIgnoreCase(String lastname); 42 | } 43 | -------------------------------------------------------------------------------- /frameworks-and-libraries/spring/src/main/java/example/complete/DefaultCustomerManagement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example.complete; 17 | 18 | import java.time.LocalDate; 19 | import java.util.Collection; 20 | 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.stereotype.Component; 23 | import org.springframework.util.Assert; 24 | 25 | /** 26 | * Default implementation of {@link CustomerManagement}. 27 | * 28 | * @author Oliver Gierke 29 | */ 30 | @Component 31 | class DefaultCustomerManagement implements CustomerManagement { 32 | 33 | private final CustomerRepository customers; 34 | 35 | /** 36 | * Creates a new {@link DefaultCustomerManagement} with the given {@link CustomerRepository}. 37 | * 38 | * @param customers must not be {@literal null}. 39 | */ 40 | @Autowired 41 | public DefaultCustomerManagement(CustomerRepository customers) { 42 | 43 | Assert.notNull(customers, "CustomerRepository must not be null!"); 44 | 45 | this.customers = customers; 46 | } 47 | 48 | /* 49 | * (non-Javadoc) 50 | * @see example.complete.CustomerManagement#registerCustomer(java.lang.String, java.lang.String, java.time.LocalDate) 51 | */ 52 | @Override 53 | public Customer registerCustomer(String firstname, String lastname, LocalDate birthday) { 54 | return customers.save(new Customer(firstname, lastname, birthday)); 55 | } 56 | 57 | /* 58 | * (non-Javadoc) 59 | * @see example.complete.CustomerManagement#findByLastnameContaining(java.lang.String) 60 | */ 61 | @Override 62 | public Collection findByLastname(String lastname) { 63 | return customers.findByLastnameContainingIgnoreCase(lastname); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /frameworks-and-libraries/spring/src/main/java/example/complete/JpaCustomerRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example.complete; 17 | 18 | import org.springframework.data.repository.Repository; 19 | 20 | /** 21 | * A {@link CustomerRepository} using Spring Data to store {@link Customer} instances in a relational database. 22 | * 23 | * @author Oliver Gierke 24 | */ 25 | interface JpaCustomerRepository extends Repository, CustomerRepository {} 26 | -------------------------------------------------------------------------------- /frameworks-and-libraries/spring/src/main/java/example/initial/Customer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example.initial; 17 | 18 | import java.time.LocalDate; 19 | 20 | import javax.persistence.Entity; 21 | import javax.persistence.GeneratedValue; 22 | import javax.persistence.Id; 23 | 24 | import org.springframework.util.ObjectUtils; 25 | 26 | /** 27 | * @author Oliver Gierke 28 | */ 29 | @Entity 30 | @SuppressWarnings("unused") 31 | public class Customer { 32 | 33 | private @GeneratedValue @Id Long id; 34 | private final String firstname, lastname; 35 | private final LocalDate birthday; 36 | 37 | Customer(String firstname, String lastname, LocalDate birthday) { 38 | 39 | this.firstname = firstname; 40 | this.lastname = lastname; 41 | this.birthday = birthday; 42 | } 43 | 44 | Customer() { 45 | this.firstname = null; 46 | this.lastname = null; 47 | this.birthday = null; 48 | } 49 | 50 | /** 51 | * @return the firstname 52 | */ 53 | public String getFirstname() { 54 | return firstname; 55 | } 56 | 57 | /** 58 | * @return the lastname 59 | */ 60 | public String getLastname() { 61 | return lastname; 62 | } 63 | 64 | public int getAge() { 65 | throw new UnsupportedOperationException(); 66 | } 67 | 68 | public boolean isAdult() { 69 | throw new UnsupportedOperationException(); 70 | } 71 | 72 | /* 73 | * (non-Javadoc) 74 | * @see java.lang.Object#equals(java.lang.Object) 75 | */ 76 | @Override 77 | public boolean equals(Object obj) { 78 | 79 | if (this == obj) { 80 | return true; 81 | } 82 | 83 | if (!(obj instanceof Customer)) { 84 | return false; 85 | } 86 | 87 | Customer that = (Customer) obj; 88 | 89 | return this.id == null ? false : this.id.equals(that.id); 90 | } 91 | 92 | /* 93 | * (non-Javadoc) 94 | * @see java.lang.Object#hashCode() 95 | */ 96 | @Override 97 | public int hashCode() { 98 | return ObjectUtils.nullSafeHashCode(this.id); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /frameworks-and-libraries/spring/src/main/java/example/initial/CustomerManagement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example.initial; 17 | 18 | import java.time.LocalDate; 19 | import java.util.Collection; 20 | 21 | /** 22 | * Service interface to manage {@link Customer}s. 23 | * 24 | * @author Oliver Gierke 25 | */ 26 | public interface CustomerManagement { 27 | 28 | /** 29 | * Registers a {@link Customer} with the given firstname, lastname and birthday in the system. 30 | * 31 | * @param firstname must not be {@literal null} or empty. 32 | * @param lastname must not be {@literal null} or empty. 33 | * @param birthday must not be {@literal null}. 34 | * @return 35 | */ 36 | Customer registerCustomer(String firstname, String lastname, LocalDate birthday); 37 | 38 | /** 39 | * Returns all {@link Customer}s that have a lastname containing the given snippet. 40 | * 41 | * @param part can be {@literal null} or empty. 42 | * @return 43 | */ 44 | Collection findByLastname(String part); 45 | } 46 | -------------------------------------------------------------------------------- /frameworks-and-libraries/spring/src/main/java/example/initial/CustomerRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example.initial; 17 | 18 | import java.util.Collection; 19 | 20 | /** 21 | * Interface to define a repository of {@link Customer}s. 22 | * 23 | * @author Oliver Gierke 24 | */ 25 | public interface CustomerRepository { 26 | 27 | /** 28 | * Saves the given {@link Customer}. 29 | * 30 | * @param customer must not be {@literal null}. 31 | * @return 32 | */ 33 | Customer save(Customer customer); 34 | 35 | /** 36 | * Returns all {@link Customer}s with a lastname containing the given snippet independent of case. 37 | * 38 | * @param lastname can be {@literal null} or empty. 39 | * @return 40 | */ 41 | Collection findByLastnameContainingIgnoreCase(String lastname); 42 | } 43 | -------------------------------------------------------------------------------- /frameworks-and-libraries/spring/src/main/java/example/initial/DefaultCustomerManagement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example.initial; 17 | 18 | import java.time.LocalDate; 19 | import java.util.Collection; 20 | 21 | /** 22 | * Default implementation of {@link CustomerManagement}. 23 | * 24 | * @author Oliver Gierke 25 | */ 26 | class DefaultCustomerManagement implements CustomerManagement { 27 | 28 | /* 29 | * (non-Javadoc) 30 | * @see example.complete.CustomerManagement#registerCustomer(java.lang.String, java.lang.String, java.time.LocalDate) 31 | */ 32 | @Override 33 | public Customer registerCustomer(String firstname, String lastname, LocalDate birthday) { 34 | throw new UnsupportedOperationException(); 35 | } 36 | 37 | /* 38 | * (non-Javadoc) 39 | * @see example.complete.CustomerManagement#findByLastnameContaining(java.lang.String) 40 | */ 41 | @Override 42 | public Collection findByLastname(String lastname) { 43 | throw new UnsupportedOperationException(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /frameworks-and-libraries/spring/src/test/java/example/complete/CustomerUnitTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example.complete; 17 | 18 | import static org.hamcrest.CoreMatchers.*; 19 | import static org.junit.Assert.*; 20 | 21 | import java.time.LocalDate; 22 | 23 | import org.junit.Rule; 24 | import org.junit.Test; 25 | import org.junit.rules.ExpectedException; 26 | 27 | /** 28 | * Unit tests for {@link Customer}. 29 | * 30 | * @author Oliver Gierke 31 | */ 32 | public class CustomerUnitTests { 33 | 34 | public @Rule ExpectedException exception = ExpectedException.none(); 35 | 36 | @Test 37 | public void rejectsNullFirstname() { 38 | 39 | exception.expect(IllegalArgumentException.class); 40 | exception.expectMessage("Firstname"); 41 | 42 | new Customer(null, "Matthews", LocalDate.of(1967, 1, 9)); 43 | } 44 | 45 | @Test 46 | public void rejectsNullLastname() { 47 | 48 | exception.expect(IllegalArgumentException.class); 49 | exception.expectMessage("Lastname"); 50 | 51 | new Customer("Dave", null, LocalDate.of(1967, 1, 9)); 52 | } 53 | 54 | @Test 55 | public void rejectsBirthdayNotInThePast() { 56 | 57 | exception.expect(IllegalArgumentException.class); 58 | exception.expectMessage("Date of birth"); 59 | 60 | new Customer("Dave", "Matthews", LocalDate.now()); 61 | } 62 | 63 | @Test 64 | public void calculatesAgeCorrectly() { 65 | 66 | Customer customer = new Customer("Dave", "Matthews", LocalDate.of(1967, 1, 9)); 67 | 68 | assertThat(customer.getAge(), is(48)); 69 | } 70 | 71 | @Test 72 | public void detectsAdultCorrectly() { 73 | 74 | Customer customer = new Customer("Dave", "Matthews", LocalDate.of(1967, 1, 9)); 75 | 76 | assertThat(customer.isAdult(), is(true)); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /frameworks-and-libraries/spring/src/test/java/example/complete/DefaultCustomerManagementIntegrationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example.complete; 17 | 18 | import static org.hamcrest.Matchers.*; 19 | import static org.junit.Assert.*; 20 | 21 | import java.time.LocalDate; 22 | import java.util.Collection; 23 | 24 | import org.junit.Test; 25 | import org.junit.runner.RunWith; 26 | import org.springframework.beans.factory.annotation.Autowired; 27 | import org.springframework.boot.test.SpringApplicationConfiguration; 28 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 29 | 30 | /** 31 | * Integration test for {@link CustomerManagement}. 32 | * 33 | * @author Oliver Gierke 34 | */ 35 | @RunWith(SpringJUnit4ClassRunner.class) 36 | @SpringApplicationConfiguration(classes = Application.class) 37 | public class DefaultCustomerManagementIntegrationTests { 38 | 39 | @Autowired CustomerManagement customerManagement; 40 | 41 | @Test 42 | public void findsCustomerByLastame() { 43 | 44 | Customer customer = customerManagement.registerCustomer("Dave", "Matthews", LocalDate.of(1967, 1, 9)); 45 | Collection result = customerManagement.findByLastname("ma"); 46 | 47 | assertThat(result, contains(customer)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /frameworks-and-libraries/spring/src/test/java/example/complete/DefaultCustomerManagementUnitTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example.complete; 17 | 18 | import static org.hamcrest.Matchers.*; 19 | import static org.junit.Assert.*; 20 | 21 | import java.time.LocalDate; 22 | 23 | import org.junit.Test; 24 | import org.mockito.Mockito; 25 | 26 | /** 27 | * Unit test for {@link DefaultCustomerManagement}. 28 | * 29 | * @author Oliver Gierke 30 | */ 31 | public class DefaultCustomerManagementUnitTests { 32 | 33 | @Test 34 | public void storesRegisteredCustomerInRepositoryViaStub() { 35 | 36 | InMemoryCustomerRepository customers = new InMemoryCustomerRepository(); 37 | CustomerManagement customerManagement = new DefaultCustomerManagement(customers); 38 | 39 | Customer result = customerManagement.registerCustomer("Dave", "Matthews", LocalDate.of(1967, 1, 9)); 40 | 41 | assertThat(customers.findAll(), contains(result)); 42 | } 43 | 44 | @Test 45 | public void storesRegisteredCustomerInRepositoryViaMock() { 46 | 47 | CustomerRepository customers = Mockito.mock(CustomerRepository.class); 48 | CustomerManagement customerManagement = new DefaultCustomerManagement(customers); 49 | 50 | customerManagement.registerCustomer("Dave", "Matthews", LocalDate.of(1967, 1, 9)); 51 | 52 | Mockito.verify(customers, Mockito.times(1)).save(Mockito.any(Customer.class)); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /frameworks-and-libraries/spring/src/test/java/example/complete/InMemoryCustomerRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example.complete; 17 | 18 | import java.util.Collection; 19 | import java.util.Collections; 20 | import java.util.HashSet; 21 | import java.util.stream.Collectors; 22 | 23 | /** 24 | * {@link CustomerRepository} implementation that stores {@link Customer}s in a {@link HashSet}. 25 | * 26 | * @author Oliver Gierke 27 | */ 28 | class InMemoryCustomerRepository implements CustomerRepository { 29 | 30 | private final Collection customers = new HashSet<>(); 31 | 32 | /** 33 | * Returns all {@link Customer}s currently available. 34 | * 35 | * @return 36 | */ 37 | public Collection findAll() { 38 | return Collections.unmodifiableCollection(customers); 39 | } 40 | 41 | /* 42 | * (non-Javadoc) 43 | * @see example.complete.CustomerRepository#save(example.complete.Customer) 44 | */ 45 | public Customer save(Customer customer) { 46 | 47 | this.customers.add(customer); 48 | return customer; 49 | } 50 | 51 | /* 52 | * (non-Javadoc) 53 | * @see example.complete.CustomerRepository#findByLastnameContains(java.lang.String) 54 | */ 55 | @Override 56 | public Collection findByLastnameContainingIgnoreCase(String lastname) { 57 | 58 | return customers.stream().// 59 | filter(customer -> customer.getLastname().toLowerCase().contains(lastname.toLowerCase())).// 60 | collect(Collectors.toSet()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /frameworks-and-libraries/spring/src/test/java/example/initial/CustomerUnitTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example.initial; 17 | 18 | import example.complete.Customer; 19 | 20 | import org.junit.Ignore; 21 | import org.junit.Rule; 22 | import org.junit.Test; 23 | import org.junit.rules.ExpectedException; 24 | 25 | /** 26 | * Unit tests for {@link Customer}. 27 | * 28 | * @author Oliver Gierke 29 | */ 30 | @Ignore 31 | public class CustomerUnitTests { 32 | 33 | public @Rule ExpectedException exception = ExpectedException.none(); 34 | 35 | @Test 36 | public void rejectsNullFirstname() { 37 | 38 | } 39 | 40 | @Test 41 | public void rejectsNullLastname() { 42 | 43 | } 44 | 45 | @Test 46 | public void rejectsBirthdayNotInThePast() { 47 | 48 | } 49 | 50 | @Test 51 | public void calculatesAgeCorrectly() { 52 | 53 | } 54 | 55 | @Test 56 | public void detectsAdultCorrectly() { 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /frameworks-and-libraries/spring/src/test/java/example/initial/DefaultCustomerManagementIntegrationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example.initial; 17 | 18 | import org.junit.Ignore; 19 | import org.junit.Test; 20 | 21 | /** 22 | * Integration test for {@link CustomerManagement}. 23 | * 24 | * @author Oliver Gierke 25 | */ 26 | @Ignore 27 | public class DefaultCustomerManagementIntegrationTests { 28 | 29 | @Test 30 | public void findsCustomerByLastame() { 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /frameworks-and-libraries/spring/src/test/java/example/initial/DefaultCustomerManagementUnitTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example.initial; 17 | 18 | import org.junit.Ignore; 19 | import org.junit.Test; 20 | 21 | /** 22 | * Unit test for {@link DefaultCustomerManagement}. 23 | * 24 | * @author Oliver Gierke 25 | */ 26 | @Ignore 27 | public class DefaultCustomerManagementUnitTests { 28 | 29 | @Test 30 | public void storesRegisteredCustomerInRepositoryViaStub() { 31 | 32 | } 33 | 34 | @Test 35 | public void storesRegisteredCustomerInRepositoryViaMock() { 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /index/index.adoc: -------------------------------------------------------------------------------- 1 | = Lectures 2 | Oliver Drotbohm 3 | :revdate: {docdatetime} 4 | :sectids!: 5 | :sectanchors: true 6 | 7 | [[htw]] 8 | == HTW Dresden 9 | * link:spring/[Spring – A Java Application Framework (Script)], Slides: link:spring/spring-a-java-application-framework.pdf[PDF] 10 | 11 | [[se]] 12 | == TU Dresden – Software Engineering - Summer semester 13 | * link:overview/intro.pdf[Overview / Intro] 14 | * link:java-tooling/[Fundamentals of Java Tooling] 15 | * link:collaborative-sd/[Collaborative Software Development] 16 | * link:frameworks-and-libraries/[Frameworks and Libraries] 17 | * link:ddd-and-spring/[Domain-Driven Design and Spring] 18 | 19 | [[se-lab]] 20 | == TU Dresden – Software Engineering Lab - Winter semester 21 | * link:spring-webapps/[Web applications with Java and Spring] 22 | * link:spring-webapps-old/[Web applications with Java and Spring (until WS 2015)] 23 | -------------------------------------------------------------------------------- /java-tooling/images/breakpoints_view.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/java-tooling/images/breakpoints_view.gif -------------------------------------------------------------------------------- /java-tooling/images/console_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/java-tooling/images/console_view.png -------------------------------------------------------------------------------- /java-tooling/images/debug_view.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/java-tooling/images/debug_view.gif -------------------------------------------------------------------------------- /java-tooling/images/display_view.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/java-tooling/images/display_view.gif -------------------------------------------------------------------------------- /java-tooling/images/eclipse-maven-dependency-hierarchy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/java-tooling/images/eclipse-maven-dependency-hierarchy.png -------------------------------------------------------------------------------- /java-tooling/images/eclipse-maven-sources.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/java-tooling/images/eclipse-maven-sources.png -------------------------------------------------------------------------------- /java-tooling/images/junit_view.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/java-tooling/images/junit_view.gif -------------------------------------------------------------------------------- /java-tooling/images/moneta-repository.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/java-tooling/images/moneta-repository.png -------------------------------------------------------------------------------- /java-tooling/images/new-maven-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/java-tooling/images/new-maven-project.png -------------------------------------------------------------------------------- /java-tooling/images/variable_view.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/java-tooling/images/variable_view.gif -------------------------------------------------------------------------------- /java-tooling/index.adoc: -------------------------------------------------------------------------------- 1 | = Fundamentals of Java Development Tooling 2 | Oliver Drotbohm 3 | :revdate: {docdatetime} 4 | :revremark: This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 5 | :numbered: 6 | :experimental: 7 | :source-highlighter: prettify 8 | :sectids!: 9 | :sectanchors: true 10 | :icons: font 11 | :toc: left 12 | :toclevels: 3 13 | :livebase: http://static.odrotbohm.de/lectures 14 | 15 | image:https://travis-ci.org/odrotbohm/lectures.svg?branch=master["Build Status", link="https://travis-ci.org/odrotbohm/lectures"] 16 | 17 | :numbered!: 18 | [preface] 19 | [[intro]] 20 | == Introduction 21 | 22 | This script should help you to get started with the fundamental tooling to support you when developing Java applications. Well cover the basic setup and usage of integrated development environments (IDEs) and build systems. 23 | 24 | NOTE: Sections marked with this icon icon:lightbulb-o[] will define tasks that you should try yourself to get a better feel for the way things work. 25 | 26 | [[intro.resources]] 27 | === Resources 28 | 29 | * https://github.com/odrotbohm/lectures[Script repository] - in case you want to file tickets for improvements. 30 | * link:{livebase}/java-tooling[Script] - the rendered version of it. 31 | 32 | [[intro.objectives]] 33 | === Objectives 34 | 35 | After working through this script you should've done -- or at least be able to do the following things: 36 | 37 | * Install a JDK, Maven and Eclipse (or STS) on your machine and run them successfully. 38 | * Create a Maven based Java project in Eclipse. 39 | * Create classes and methods, use the code completion facilities to write code, rename methods etc. 40 | * Run and debug Java code in Eclipse, set break points and step through the code. 41 | * Run the project build from the command line. 42 | 43 | :numbered: 44 | [[prerequisites]] 45 | == Infrastructure prerequisites 46 | * http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html[JDK 8] 47 | * https://eclipse.org/downloads/[Eclipse] / https://spring.io/tools/sts[STS] - Adds Spring Framework specific tooling 48 | * http://maven.apache.org/download.cgi[Maven] (optional) -> to execute the build at the command line (for continuous integration, release builds) 49 | 50 | [[quick-start]] 51 | == Quick start 52 | 53 | We're gonna create a new project within Eclipse here. This will be a Maven based project (see <>) for details. 54 | 55 | 1. Start Eclipse/STS. 56 | 2. Create a new Maven project. 57 | + 58 | * menu:File[New > Other…] or kbd:[⌘+N] 59 | * type "Maven", select "Maven Project", btn:[Next] 60 | * check "Create simple project", btn:[Next] 61 | * enter group and artifact identifier, e.g. `demo` and `sample-project` (artifact identifier will become project name) 62 | + 63 | image::images/new-maven-project.png[] 64 | + 65 | 3. Create a Java class. 66 | + 67 | * Select `src/main/java`, kbd:[⌥+⌘+N], type `Class`, kbd:[⏎] or right-click, menu:New[Class] 68 | * define a package of `demo`, type name `HelloWorld` 69 | 70 | 4. Add a main method. 71 | + 72 | [source, java] 73 | ---- 74 | public static void main(String... args) { 75 | System.out.println("Hello world!"); 76 | } 77 | ---- 78 | 79 | 5. Run the class. 80 | + 81 | * Select the class in the project or package explorer (left column). 82 | * kbd:[⌥+⌘+X], kbd:[J] or right-click, select menu:Run As[Java Application]. 83 | * see `Hello world!` printed to the console. 84 | 85 | [[ide]] 86 | == Eclipse 87 | 88 | Eclipse is an integrated development environment (IDE). It's primarily used for Java development but can be equipped with plugins to develop software in other languages as well. Other IDEs available for Java development are https://www.jetbrains.com/idea/download/[IntelliJ IDEA] and https://netbeans.org/downloads/[Netbeans]. 89 | 90 | [[ide.advantages]] 91 | === Advantages of using an IDE 92 | 93 | * *Quick code base navigation* - IDEs allow you to navigate the code base in a semantic way by referring to types, methods, fields etc. 94 | * *Code editing / refactoring* - Because the code you write is structurally and semantically analyzed, the IDE can help you write code quicker and avoid typing mistakes, manage the needed imports analyzing your classpath. Also changes to the code base can be executed in a way that the IDE makes sure they're applied consistently like method or field renames. Also, IDEs usually compile code on the fly and are even capable of partially compiling code bases. This provides instant feedback about programming mistakes. 95 | + 96 | Eclipse provides so called "Save actions" which -- as the name suggests -- are executed every time you save a source file. They allow you to automatically manage imports, apply certain formatting settings and tweak the code according to pre-defined rules. 97 | * *Executing code* - IDEs allow you to run the application or parts of it right from within the IDE. This especially comes in handy when working with test cases. 98 | 99 | [[ide.views-and-perspectives]] 100 | === Views and perspectives 101 | 102 | Core UI concepts are views that can be arranged in a certain way to make up a perspective. 103 | 104 | * Open a view: menu:Window[Show view > Other…] 105 | * Open a perspective: menu:Window[Open perspective > Other…] 106 | * Switch between perspectives: kbd:[⌘+F8] 107 | 108 | * Spring / Java perspective 109 | ** Project explorer (usually on the left side) - browse projects 110 | ** Editors - main screen, multiple tabs 111 | ** image:images/console_view.png[] Console - output of the program, Maven build information 112 | ** image:images/junit_view.gif[] JUnit - test results, test execution 113 | * Debug perspective 114 | ** image:images/debug_view.gif[] Debug view - process information, stack 115 | ** image:images/variable_view.gif[] Variables - variable context at the current breakpoint 116 | ** image:images/breakpoints_view.gif[] Breakpoints - break points currently defined 117 | ** image:images/display_view.gif[] Display - live code execution within the current context 118 | 119 | [[build]] 120 | == Project build 121 | 122 | Compiling a set of Java classes is usually not enough to actually run software in production. The application needs to be assembled, integration tested, documentation needs to be created, bundled and published. This is where build systems come into play. 123 | 124 | A build system allows users to declare and configure individual elements of a build process. The predominant build systems in the Java space are https://maven.apache.org[Maven] and https://gradle.org/[Gradle]. Maven is currently the most widely used one although Gradle's been gaining a lot of traction recently. The latter is quite a bit more flexible when it comes to more advanced requirements in a project build. As we're not going to need this flexibility here we're going to stay with Maven for this lecture. 125 | 126 | [[build.maven]] 127 | === Maven 128 | 129 | https://maven.apache.org[Maven] is currently the predominant tool to build Java based software projects. It's centered around the notion of a Project Object Model (POM) to describe the project, its dependencies and which steps shall be executed during the build. 130 | 131 | [NOTE] 132 | ==== 133 | You can install Maven by either 134 | 135 | * downloading it from the official https://maven.apache.org/download.cgi[website]. 136 | * with https://brew.sh[Homebrew] if you're using Mac OSX. Simply run `brew install maven` on the command line. 137 | * or you favorite linux package manager (search for `maven`). 138 | ==== 139 | 140 | The build execution is backed by a so called https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html[lifecycle] which is basically a predefined set of steps to be executed sequentially. Depending on the type of project that's being built a set of default plugins is assigned to the individual steps. Here's an incomplete list of the most important lifecycle phases: 141 | 142 | * `compile` - compile sources (production and tests) 143 | * `test` - execute test code (usually unit and fine grained integration tests) 144 | * `package` - package the artifacts (JARs, WARs etc.) 145 | * `integration-test` - execute high-level integration tests 146 | * `install` - install the artifact into the local repository 147 | * `deploy` - deploy the artifact into the remote repository, distribute resources 148 | 149 | [TIP] 150 | ==== 151 | Open up the console and navigate to the folder you've created the quick start project in the first place. Make sure you've got Maven installed (see <> for details). Run 152 | 153 | [source, bash] 154 | ---- 155 | $ mvn clean package 156 | [INFO] Scanning for projects... 157 | [INFO] 158 | [INFO] ------------------------------------------------------------------------ 159 | [INFO] Building sample-project 1.0.0-SNAPSHOT 160 | [INFO] ------------------------------------------------------------------------ 161 | [INFO] 162 | [INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ sample-project --- 163 | [INFO] Deleting …/se-demo/target 164 | [INFO] 165 | [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ sample-project --- 166 | [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! 167 | [INFO] Copying 0 resource 168 | [INFO] 169 | [INFO] --- maven-compiler-plugin:3.3:compile (default-compile) @ sample-project --- 170 | [INFO] Changes detected - recompiling the module! 171 | [WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent! 172 | [INFO] Compiling 1 source file to …/se-demo/target/classes 173 | [INFO] 174 | [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ sample-project --- 175 | [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! 176 | [INFO] Copying 0 resource 177 | [INFO] 178 | [INFO] --- maven-compiler-plugin:3.3:testCompile (default-testCompile) @ sample-project --- 179 | [INFO] Nothing to compile - all classes are up to date 180 | [INFO] 181 | [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ sample-project --- 182 | [INFO] 183 | [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ sample-project --- 184 | [INFO] Building jar: …/se-demo/target/sample-project-1.0.0-SNAPSHOT.jar 185 | [INFO] ------------------------------------------------------------------------ 186 | [INFO] BUILD SUCCESS 187 | [INFO] ------------------------------------------------------------------------ 188 | [INFO] Total time: 0.905 s 189 | [INFO] Finished at: 2015-05-07T13:16:33+02:00 190 | [INFO] Final Memory: 17M/305M 191 | [INFO] ------------------------------------------------------------------------ 192 | $ 193 | ---- 194 | ==== 195 | 196 | If you'd like to see the build in action for a real-world project that produces a bit more output, you can try this (requires https://git-scm.com[Git] and Maven installed on your machine). 197 | 198 | [source, bash] 199 | ---- 200 | $ git clone https://github.com/st-tu-dresden/guestbook 201 | $ cd guestbook 202 | $ mvn clean install 203 | ---- 204 | 205 | See the project build executed in the console, tests being executed. 206 | 207 | [build.maven.configuration] 208 | ==== Build configuration 209 | 210 | The build is configured using an XML document called `pom.xml` in the project root. It contains the artifact coordinates (see <> for details), project metadata, dependencies and build plugin configuration. 211 | 212 | For the sample project we created above the `pom.xml` could look something like this: 213 | 214 | [source, xml] 215 | ---- 216 | 220 | 221 | 4.0.0 222 | 223 | demo <1> 224 | sample-project 225 | 1.0.0-SNAPSHOT 226 | 227 | <2> 228 | 229 | … 230 | 231 | 232 | 233 | 234 | 235 | <3> 236 | org.apache.maven.plugins 237 | maven-compiler-plugin 238 | 3.3 239 | 240 | 1.8 241 | 1.8 242 | 243 | 244 | 245 | 246 | 247 | 248 | ---- 249 | <1> Project artifact coordinates consisting of group and artifact identifier as well as a version. 250 | <2> Definition of project dependencies. 251 | <3> Configuration of a build plugin (configuring the Java compiler to use Java 8 here). 252 | 253 | TIP: Open up the `pom.xml` you've created during the quick start, switch to the XML view and trigger code completion using kbd:[⌃+Space] to get a feel of the completion support the IDE provides. 254 | 255 | IDEs support Maven out of the box and usually derive all necessary project settings from the POM. 256 | Thus changing something about the project is usually all about tweaking the POM and the refreshing the project setup using kbd:[⌥+F5] (or right-click, menu:Maven[Update Project…]). 257 | Note, that the project root folder needs to be imported which might be a different one than the repository one in case multiple projects are contained in the repository. 258 | 259 | TIP: Remove the compiler plugin declaration from the `pom.xml` and refresh the project. See how the JRE System Library node changes back to Java 1.5 (Maven's default). Re-add the plugin declaration, update again and see how it changes back to 1.8 due to our definition of source and target level. 260 | 261 | [[build.maven.project-structure]] 262 | ==== Project structure 263 | 264 | Maven defines a common project structure to make it easy to decide which files go where. By default Maven uses the following structure: 265 | 266 | * `src/main/java` - Production code. 267 | * `src/main/resources` - Production configuration files and resources. 268 | * `src/test/java` - Test code, tests to be executed have to be named `…Tests`. 269 | * `src/test/resources` - Test configuration files and resources. 270 | 271 | [[build.maven.dependency-management]] 272 | ==== Dependency management 273 | 274 | One of the primary factors of Java being the most widely used programming language in the world is the ecosystem of libraries available. Almost any kind of technical problem has an implemented solution available. So a Java application will - almost by definition - use quite a few of these already existing libraries. 275 | 276 | In the early days of Java these libraries had to be manually downloaded, put into the project and bundled with the application. These days, build systems provide means to logically define the dependencies of an application and take care of resolving the physical artifacts and packaging them with the application. The artifacts are held in a so called repository, the primary one to refer to being http://search.maven.org/[Maven Central]. 277 | 278 | In a Maven project, dependencies are declared within a `` element in `pom.xml`: 279 | 280 | .Declaring dependencies in a Maven POM 281 | ==== 282 | [source, xml] 283 | ---- 284 | 288 | 289 | … 290 | 291 | 292 | <1> 293 | org.javamoney 294 | moneta 295 | 1.0-RC3 296 | 297 | 298 | 299 | 300 | ---- 301 | <1> Defines a dependency to the https://github.com/JavaMoney/jsr354-ri[Moneta] library. 302 | ==== 303 | 304 | A dependency is expressed by listing the group and artifact identifier alongside the version of the library. The exact coordinates and available versions can be found by looking up the artifact in the http://search.maven.org/[Maven Central index]. 305 | 306 | When the Maven build is run, the list of declared dependencies is consolidated and - if the physical artifacts are not available on the local machine already - obtained from the remote repository and stored in the local repository. The repository is located in `~/.m2/repository`. 307 | 308 | .The structure of the local Maven repository 309 | ==== 310 | image::images/moneta-repository.png[] 311 | ==== 312 | 313 | As you can see the group identifier is expanded into folders, followed by a folder for the artifact identifier and one for the version. The repository not only contains the binary Java Archive (JAR) but also additional JARs for containing the sources and JavaDoc. 314 | 315 | [NOTE] 316 | ==== 317 | The sources and JavaDoc being present is caused by the IDE being configured to retrieve these additional artifacts. To enable this, check the Eclipse settings and enable "Download Artifact Sources" and "Download Artifact JavaDoc" in the section "Maven". 318 | 319 | image::images/eclipse-maven-sources.png[] 320 | ==== 321 | 322 | ===== Transitive dependencies 323 | 324 | Maven dependencies can of course in turn have dependencies themselves. This creates a tree of dependencies that will all be resolved by Maven automatically. To get an impression of the structure of dependencies, open the `pom.xml` in Eclipse and select the "Dependency hierarchy" tab at the bottom of the editor. 325 | 326 | image::images/eclipse-maven-dependency-hierarchy.png[] 327 | 328 | As you can see, Moneta depends on `money-api` and `javax.annotation-api` in turn and the dependencies have been resolved. Also, we've declared another dependency to JUnit in version 4.12. We defined it to be a test scope dependency. This means it will not be available when compiling production code. Also it will not be packaged with the application as it's not needed at runtime but only for the execution of tests. 329 | 330 | [appendix] 331 | [[appendix.recommended-reading]] 332 | == Recommended literature 333 | 334 | * Head First Java, 2nd edition. Sierra. O'Reilly, 2005. https://www.amazon.de/Head-First-Java-2nd-Second/dp/B008GB1BU8[Book at Amazon] 335 | * Java by Comparison: Become a Java Craftsman in 70 Examples. Harrer, Lenhard, Dietz. O'Reilly, 2018. https://www.amazon.de/Java-Comparison-Become-Craftsman-Examples/dp/1680502875[Book at Amazon] 336 | 337 | [appendix] 338 | [[appendix.shortcuts]] 339 | == Keyboard shortcuts 340 | 341 | === Code base navigation 342 | 343 | * kbd:[⌘+⇧+T] - Open type (supports `*` and camel case lookups, e.g `ArLi` matches `ArrayList`) 344 | * kbd:[⌘+⇧+R] - Open resource (like Open Type but also includes non-code source files) 345 | * kbd:[⌘+O] - Outline (lists class methods and properties, type right away and hit btn:[Enter] to jump to the currently selected location). 346 | * kbd:[⌘+T] - Show type hierarchy. 347 | * kbd:[⌘+⇧+G] - Search for references in the workspace. 348 | * kbd:[⌃+⌥+H] - Find code that calls the selected method. 349 | 350 | === Code editing / refactoring 351 | 352 | * kbd:[⌃+Space] - Code completion for methods, type names, variables 353 | * kbd:[⌘+1] - Quick fix 354 | * kbd:[⌘+D] - Delete line. 355 | * kbd:[⌥+↑] - Move line up. 356 | * kbd:[⌥+↓] - Move line down. 357 | * kbd:[⌥+⌘+T] - Refactoring… (select menu item). 358 | * kbd:[⌥+⌘+R] - Quick rename. 359 | 360 | === Executing code 361 | 362 | * kbd:[⌥+⌘+X] - eXecute… 363 | ** kbd:[J] - Java Application. 364 | ** kbd:[T] - Test case. 365 | * kbd:[⌥+⇧+D] - Debug… 366 | ** kbd:[J] - Java Application. 367 | ** kbd:[T] - Test case. 368 | * kbd:[⌘+⇧+F11] - eXecute last launch configuration. 369 | * kbd:[⌘+F11] - Debug last launch configuration. 370 | 371 | === Maven 372 | 373 | * kbd:[⌥+F5] - Update project configuration 374 | 375 | === Miscellaneous 376 | ** kbd:[⌘+3] - Quick access (to trigger almost any IDE functionality) 377 | 378 | [appendix] 379 | [[appendix.license]] 380 | == License 381 | image::https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png[link="http://creativecommons.org/licenses/by-nc-sa/4.0/"] 382 | -------------------------------------------------------------------------------- /layout/.gitignore: -------------------------------------------------------------------------------- 1 | !js/ 2 | !css/ 3 | -------------------------------------------------------------------------------- /layout/css/lectures.css: -------------------------------------------------------------------------------- 1 | @import url(spring.css); 2 | 3 | body.toc2 { 4 | padding-top: 0; 5 | } 6 | 7 | /* Remove header image and add spacing for TOC */ 8 | @media only screen and (min-width: 768px) { 9 | 10 | #content { 11 | padding-left: 8em; 12 | } 13 | 14 | #header { 15 | padding-left: 8em; 16 | padding-right: 0; 17 | } 18 | 19 | #header::after { 20 | background-image: none; 21 | } 22 | 23 | body.toc2 #toc.toc2 { 24 | margin-top: 5em !important; 25 | } 26 | } 27 | 28 | /* Remove header image */ 29 | @media only screen and (min-width: 1290px) { 30 | 31 | #content { 32 | padding-left: 20em; 33 | } 34 | 35 | #header { 36 | padding-left: 20em; 37 | padding-right: 0; 38 | } 39 | 40 | #header::after { 41 | background-image: none; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /layout/js/toc.js: -------------------------------------------------------------------------------- 1 | var toctitle = document.getElementById('toctitle'); 2 | var path = window.location.pathname; 3 | if (toctitle != null) { 4 | var oldtoc = toctitle.nextElementSibling; 5 | var newtoc = document.createElement('div'); 6 | newtoc.setAttribute('id', 'tocbot'); 7 | newtoc.setAttribute('class', 'js-toc desktop-toc'); 8 | oldtoc.setAttribute('class', 'mobile-toc'); 9 | oldtoc.parentNode.appendChild(newtoc); 10 | tocbot.init({ 11 | contentSelector: '#content', 12 | headingSelector: 'h1, h2, h3, h4, h5', 13 | positionFixedSelector: 'body', 14 | fixedSidebarOffset: 90, 15 | smoothScroll: false 16 | }); 17 | 18 | var link = document.createElement("a"); 19 | if (document.getElementById('index-link')) { 20 | indexLinkElement = document.querySelector('#index-link > p > a'); 21 | linkHref = indexLinkElement.getAttribute("href"); 22 | link.setAttribute("href", linkHref); 23 | } else { 24 | link.setAttribute("href", "../index.html"); 25 | } 26 | link.innerHTML = " Back to lectures"; 27 | var block = document.createElement("div"); 28 | block.setAttribute('class', 'back-action'); 29 | block.appendChild(link); 30 | var toc = document.getElementById('toc'); 31 | var next = document.getElementById('toctitle').nextElementSibling; 32 | toc.insertBefore(block, next); 33 | } 34 | 35 | /* 36 | 37 | var headerHtml = '
\n' + 38 | '

\n' + 39 | '\n' + 41 | '\n' + 42 | '\n' + 43 | '\n' + 49 | '\n' + 50 | '\n' + 54 | '\n' + 57 | '\n' + 59 | '\n' + 61 | '\n' + 64 | '\n' + 69 | '\n' + 71 | '\n' + 72 | '\n' + 73 | '\n' + 74 | '\n' + 75 | '\n' + 76 | '

\n' + 77 | '
'; 78 | 79 | headerHtml = 'Back to lectures'; 80 | 81 | var header = document.createElement("div"); 82 | header.innerHTML = headerHtml; 83 | document.body.insertBefore(header, document.body.firstChild); 84 | */ 85 | -------------------------------------------------------------------------------- /layout/js/tocbot/tocbot.css: -------------------------------------------------------------------------------- 1 | .toc{overflow-y:auto}.toc>.toc-list{overflow:hidden;position:relative}.toc>.toc-list li{list-style:none}.toc-list{margin:0;padding-left:10px}a.toc-link{color:currentColor;height:100%}.is-collapsible{max-height:1000px;overflow:hidden;transition:all 300ms ease-in-out}.is-collapsed{max-height:0}.is-position-fixed{position:fixed !important;top:0}.is-active-link{font-weight:700}.toc-link::before{background-color:#EEE;content:' ';display:inline-block;height:inherit;left:0;margin-top:-1px;position:absolute;width:2px}.is-active-link::before{background-color:#54BC4B} 2 | -------------------------------------------------------------------------------- /layout/js/tocbot/tocbot.min.js: -------------------------------------------------------------------------------- 1 | !function(e){function t(o){if(n[o])return n[o].exports;var l=n[o]={i:o,l:!1,exports:{}};return e[o].call(l.exports,l,l.exports,t),l.l=!0,l.exports}var n={};t.m=e,t.c=n,t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:o})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=0)}([function(e,t,n){(function(o){var l,i,s;!function(n,o){i=[],l=o(n),void 0!==(s="function"==typeof l?l.apply(t,i):l)&&(e.exports=s)}(void 0!==o?o:this.window||this.global,function(e){"use strict";function t(){for(var e={},t=0;te.fixedSidebarOffset?-1===n.className.indexOf(e.positionFixedClass)&&(n.className+=h+e.positionFixedClass):n.className=n.className.split(h+e.positionFixedClass).join("")}function s(t){var n=document.documentElement.scrollTop||f.scrollTop;e.positionFixedSelector&&i();var o,l=t;if(m&&null!==document.querySelector(e.tocSelector)&&l.length>0){d.call(l,function(t,i){if(t.offsetTop>n+e.headingsOffset+10){return o=l[0===i?i:i-1],!0}if(i===l.length-1)return o=l[l.length-1],!0});var s=document.querySelector(e.tocSelector).querySelectorAll("."+e.linkClass);u.call(s,function(t){t.className=t.className.split(h+e.activeLinkClass).join("")});var c=document.querySelector(e.tocSelector).querySelectorAll("."+e.listItemClass);u.call(c,function(t){t.className=t.className.split(h+e.activeListItemClass).join("")});var a=document.querySelector(e.tocSelector).querySelector("."+e.linkClass+".node-name--"+o.nodeName+'[href="#'+o.id+'"]');-1===a.className.indexOf(e.activeLinkClass)&&(a.className+=h+e.activeLinkClass);var p=a.parentNode;p&&-1===p.className.indexOf(e.activeListItemClass)&&(p.className+=h+e.activeListItemClass);var C=document.querySelector(e.tocSelector).querySelectorAll("."+e.listClass+"."+e.collapsibleClass);u.call(C,function(t){-1===t.className.indexOf(e.isCollapsedClass)&&(t.className+=h+e.isCollapsedClass)}),a.nextSibling&&-1!==a.nextSibling.className.indexOf(e.isCollapsedClass)&&(a.nextSibling.className=a.nextSibling.className.split(h+e.isCollapsedClass).join("")),r(a.parentNode.parentNode)}}function r(t){return-1!==t.className.indexOf(e.collapsibleClass)&&-1!==t.className.indexOf(e.isCollapsedClass)?(t.className=t.className.split(h+e.isCollapsedClass).join(""),r(t.parentNode.parentNode)):t}function c(t){var n=t.target||t.srcElement;"string"==typeof n.className&&-1!==n.className.indexOf(e.linkClass)&&(m=!1)}function a(){m=!0}var u=[].forEach,d=[].some,f=document.body,m=!0,h=" ";return{enableTocAnimation:a,disableTocAnimation:c,render:n,updateToc:s}}},function(e,t){e.exports=function(e){function t(e){return e[e.length-1]}function n(e){return+e.nodeName.split("H").join("")}function o(t){var o={id:t.id,children:[],nodeName:t.nodeName,headingLevel:n(t),textContent:t.textContent.trim()};return e.includeHtml&&(o.childNodes=t.childNodes),o}function l(l,i){for(var s=o(l),r=n(l),c=i,a=t(c),u=a?a.headingLevel:0,d=r-u;d>0;)a=t(c),a&&void 0!==a.children&&(c=a.children),d--;return r>=e.collapseDepth&&(s.isCollapsed=!0),c.push(s),c}function i(t,n){var o=n;e.ignoreSelector&&(o=n.split(",").map(function(t){return t.trim()+":not("+e.ignoreSelector+")"}));try{return document.querySelector(t).querySelectorAll(o)}catch(e){return console.warn("Element not found: "+t),null}}function s(e){return r.call(e,function(e,t){return l(o(t),e.nest),e},{nest:[]})}var r=[].reduce;return{nestHeadingsArray:s,selectHeadings:i}}},function(e,t){function n(e){function t(e){return"a"===e.tagName.toLowerCase()&&(e.hash.length>0||"#"===e.href.charAt(e.href.length-1))&&(n(e.href)===s||n(e.href)+"#"===s)}function n(e){return e.slice(0,e.lastIndexOf("#"))}function l(e){var t=document.getElementById(e.substring(1));t&&(/^(?:a|select|input|button|textarea)$/i.test(t.tagName)||(t.tabIndex=-1),t.focus())}!function(){document.documentElement.style}();var i=e.duration,s=location.hash?n(location.href):location.href;!function(){function n(n){!t(n.target)||n.target.className.indexOf("no-smooth-scroll")>-1||"#"===n.target.href.charAt(n.target.href.length-2)&&"!"===n.target.href.charAt(n.target.href.length-1)||-1===n.target.className.indexOf(e.linkClass)||o(n.target.hash,{duration:i,callback:function(){l(n.target.hash)}})}document.body.addEventListener("click",n,!1)}()}function o(e,t){function n(e){s=e-i,window.scrollTo(0,c.easing(s,r,u,d)),s 2 | 4 | 4.0.0 5 | 6 | de.odrotbohm.lectures 7 | all-lectures 8 | 0.0.1-SNAPSHOT 9 | pom 10 | 11 | All lectures 12 | 13 | 14 | spring-webapps/code 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /render.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ./scripts/prepare.sh 3 | echo "Rendering index.adoc files to HTML…" 4 | dir=$(pwd) 5 | find . -name "index.adoc" -exec echo {} \; \ 6 | -exec asciidoctor \ 7 | -a stylesdir=css \ 8 | -a stylesheet=lectures.css \ 9 | -a source-highlighter=prettify \ 10 | -a linkcss=true \ 11 | -a docinfo=shared \ 12 | -a docinfodir=$dir/layout \ 13 | -r asciidoctor-diagram {} \; 14 | -------------------------------------------------------------------------------- /scripts/prepare.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Updating JavaScript and CSS…" 3 | for f in $(find . -name 'index.adoc' -exec dirname {} \;); do 4 | rm -r ${f}/js ${f}/css; 5 | cp -R layout/js ${f}; 6 | cp -R layout/css ${f}; 7 | done; -------------------------------------------------------------------------------- /spring-webapps-old/README.adoc: -------------------------------------------------------------------------------- 1 | # State-of-the-art web application development with Java and Spring 2 | 3 | This repository contains the script for a lecture I held at TU Dresden in support of the Sofware Engineering Lab in winter semester 2014 and 2015. It's primary goal is to ramp up the students with the very basics of Java web application development. 4 | 5 | The script is written in http://www.asciidoctor.org[Asciidoctor] and can be viewed http://static.olivergierke.de/lectures/spring-webapps/index.html[here]. To build the HTML file yourself, install Asciidoctor as described http://asciidoctor.org/docs/install-toolchain/[in the documentation] and run `asciidoctor index.adoc`. 6 | -------------------------------------------------------------------------------- /spring-webapps-old/images/maven-console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/spring-webapps-old/images/maven-console.png -------------------------------------------------------------------------------- /spring-webapps-old/images/project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/spring-webapps-old/images/project.png -------------------------------------------------------------------------------- /spring-webapps-old/images/sts-components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/spring-webapps-old/images/sts-components.png -------------------------------------------------------------------------------- /spring-webapps-old/quickstart.adoc: -------------------------------------------------------------------------------- 1 | [[quickstart]] 2 | = A Java web application -- Quick start 3 | :imagesdir: images 4 | 5 | [[quickstart.objectives]] 6 | == Objectives 7 | 8 | * Learn how to create, setup and run a Java based web project 9 | * Learn how to handle a web request 10 | * Learn how to efficiently work with web related code 11 | 12 | [[quickstart.create-project]] 13 | == Create a new project 14 | 15 | .IDE centric style 16 | 17 | 1. Start STS. 18 | 2. Create a new project menu:File[New… > Other] (or kbd:[⌘+N]). 19 | 3. Select "Spring Starter Project". 20 | 4. Keep settings (details on that below). 21 | 5. Select "Web" in the Dependencies matrix. 22 | 6. Select btn:[Finish]. 23 | 24 | .Manual version 25 | 26 | 1. Browse to http://start.spring.io. 27 | 2. Keep settings (details on them below). 28 | 3. Select "Web" in the Dependencies matrix. 29 | 4. Select btn:[Generate project]. 30 | 5. Open up your IDE. 31 | 6. In Eclipse select menu:File[Import…]. 32 | 7. Select "Existing Maven Projects". 33 | 34 | .The result 35 | The result should be a Java project created in the IDE looking something like this: 36 | 37 | image::project.png[] 38 | 39 | What do we have here? 40 | 41 | * `Application.java` in `src/main/java` - The main (startable) application class. 42 | * `static` / `templates` folders in `src/main/resources` - Folders for static resources and view templates. 43 | * `application.properties` - Configuration file. 44 | * `ApplicationTests.java` - Integration test to test the application. 45 | * `pom.xml` - The POM (Project Object Model). A Maven configuration file to control project setup, build and dependencies. 46 | 47 | [[quickstart.minimal-functionality]] 48 | == Add minimal functionality and run this thing 49 | 50 | * Create a new class, e.g. in the `demo` package: Select `src/main/java`, press kbd:[⌥+⌘+N], type `Class` or select menu:File[New… > Class] to bring up the new class wizard. Enter `demo` for the package name, `WelcomeController` as type name. Edit the created class to look like this: 51 | + 52 | .A simple Spring MVC controller 53 | ==== 54 | [source, java] 55 | ---- 56 | @RestController <1> 57 | class WelcomeController { 58 | 59 | @RequestMapping("/welcome") <2> 60 | String welcome() { 61 | return "Welcome"; <3> 62 | } 63 | } 64 | ---- 65 | <1> Causes the class to be found by the Spring container and turned into a Spring bean. This is not directly caused by `@RestController` but the annotation being meta-annotated with `@Controller` which is in turn annotated with `@Component`. See more on that in <>. 66 | <2> Maps `/welcome` to the execution of the annotated method. All HTTP request to that URI will cause the method to be invoked. 67 | <3> The result to be written to the response. Using `@RestController` causes the `String` returned to be considered the response payload. 68 | ==== 69 | 70 | [NOTE] 71 | ==== 72 | When pasting this code into your IDE it might already import the right types for you. In case it doesn't and you see red underlines e.g. under `@RestController` you have a variety of options: 73 | 74 | * Navigate to right after `@RestController` and hit kbd:[⌃+Space]. This will trigger code completion suggestions and either provide you with a drop down list to choose the type from (in case multiple ones matching what you typed are on the classpath). 75 | * Alternatively try kbd:[⌘+⇧+O] (Organize imports), which should try to resolve the type tokens you used. 76 | ==== 77 | 78 | * Execute `Application.java` (kbd:[⌥+⌘+X],kbd:[J] or right click the class, menu:Run As[Java Application]). 79 | * Browse to http://localhost:8080/welcome, should give you `Welcome`. 80 | 81 | TIP: Try using kbd:[⌘+Space] while typing here and there. Eclipse will usually provide you quite useful suggestions for code completion. 82 | 83 | [[quickstart.dynamics]] 84 | == Make this a bit more dynamic 85 | 86 | TIP: If you run the application in debug mode, you can change method bodies on the fly without having to restart the app. We're going to use that mode going forward. 87 | 88 | * Change the method signature of the controller method to this: 89 | + 90 | [source, java] 91 | ---- 92 | welcome(@RequestParam(value = "name") Optional name) 93 | ---- 94 | 95 | * Restart the application in debug mode (kbd:[⇧+⌥+D],kbd:[J] or menu:Debug As[Java Application]). 96 | * Browse to `http://localhost:8080/welcome`, output should be unchanged. 97 | * Change the URI to `http://localhost:8080/welcome?name=Java`, doesn't have any effect yet. 98 | * While the application is running, change the method body to: 99 | + 100 | [source, java] 101 | ---- 102 | return "Welcome ".concat(name.orElse("World")).concat("!"); 103 | ---- 104 | 105 | * Refresh the browser tab and the response should now adapt to changes of the `name` attribute. 106 | -------------------------------------------------------------------------------- /spring-webapps-old/spring.adoc: -------------------------------------------------------------------------------- 1 | [[spring]] 2 | = Spring -- An application framework 3 | :imagesdir: images 4 | 5 | The Spring Framework footnote:spring[Spring - https://en.wikipedia.org/wiki/Spring_Framework[Wikipedia]] is an open-source, general purpose, Java-based application framework. 6 | Parts of it can be used as a library but at the very core of it it's an inversion-of-controlfootnote:ioc[Inversion of Control - https://en.wikipedia.org/wiki/Inversion_of_control[Wikipedia]] container which allows you to write easy to test application components. 7 | 8 | Beyond that it provides means to apply technical services (e.g. transactions and security) in a declarative and portable way, integrations with persistence and integration technologies as well as an MVC web framework. 9 | 10 | As a developer you usually use Spring Framework through Spring Boot, which is an opinionated add-on to automatically configure and ease the set up of a Spring based application so that you can focus on your business codefootnote:spring-philosophy[Philosophies that Shaped Successful Frameworks - https://medium.com/capital-one-tech/philosophies-that-shaped-successful-frameworks-f976781e9bd4/[Blog post]]. 11 | 12 | [[spring.ioc]] 13 | == Inversion of Control 14 | Inversion of Control (otherwise also referred to as Dependency Injection) is the software design technique that frees a component from the task to look up its collaborators and rather get those handed into the component – usually as constructor arguments. 15 | This inverts the control of this lookup from the component itself to its context (hence the name). 16 | The assembly of a component is externalized and usually taken care of by a generic framework but can -- and almost always is -- also be used manually e.g. in test cases. 17 | 18 | This approach especially improves testability of components as the collaborators that would be used in a production environment can easily be replaced with test components. 19 | 20 | [[spring.building-blocks]] 21 | == Fundamental building blocks of a Spring Boot application 22 | 23 | * *Application code* -- code you write, e.g. the `WelcomeController` in the quick start example. 24 | * *Configuration* -- code to configure the application container. Declares references to infrastructure components (e.g. the database, security) and defines how application components are found: 25 | + 26 | .An example configuration class 27 | ==== 28 | [source, java] 29 | ---- 30 | @SpringBootApplication 31 | class Application { 32 | 33 | public static void main(String... args) { 34 | SpringApplication.run(Application.class, args); 35 | } 36 | } 37 | ---- 38 | ==== 39 | 40 | This is the primary application class (hence the name) and uses `SpringApplication.run(…)` in the main method to start a Spring `ApplicationContext` and inspect the given configuration, the `Application` class itself in this case. 41 | 42 | The `@SpringBootApplication` is the annotation to enable very fundamental features of Spring boot. It actually acts like a combination of the following three annotations: 43 | 44 | * `@Configuration` -- Declares the class to contain configuration. This will cause the container to detect methods annotated with `@Bean` for you to provide custom components manually. 45 | * `@EnableAutoConfiguration` -- Enables Spring Boot's auto-configuration features to activate features based on your classpath setup. 46 | * `@ComponentScan` -- Activates the detection of Spring components for the package of the annotated class. 47 | 48 | Thus, using the single annotation, all of these features get activated at application startup. 49 | 50 | [[spring.bootstrap]] 51 | == Running a Spring Boot application 52 | 53 | The application containing a main method, makes it very easy to start it from within the IDE as the class itself becomes executable. 54 | That's very convenient during development. 55 | However, there are a couple of more ways we might want to execute the application: packaged into an executable, so that we can actually run it on a production server, and during integration test execution to verify its behavior. 56 | 57 | [[spring.bootstrap.standalone]] 58 | === Standalone 59 | 60 | * On the command line, run `mvn clean package` and run the JAR (Java application ARchive) using `java -jar target/\*.jar`. 61 | You can basically take the JAR created by the build and run that on any machine that has Java installed. 62 | 63 | WARNING: Don't be trapped by trying to use your IDE's JAR export mechanism. 64 | It's crucial for the application to work as a standalone JAR to be built using the the actual build mechanism, which applies the necessary plugins to make the JAR bootable. 65 | 66 | [[spring.bootstrap.integration-tests]] 67 | === In integration tests 68 | 69 | * One of the most common ways of executing Test cases is with an open-source library called http://junit.org[JUnit] which has both Maven and Eclipse integration. 70 | * To bootstrap the application container in an integration test the test class has to look as follows: 71 | + 72 | .Bootstrapping the Spring container from an integration test 73 | ==== 74 | [source, java] 75 | ---- 76 | @SpringBootTest 77 | @RunWith(SpringRunner.class) 78 | class ApplicationTests { … } 79 | ---- 80 | ==== 81 | * `@SpringBootTest` expresses Boot to be used during tests. The main configuration class will be auto-detected looking for a class with an `@SpringBootApplication` in the current package and all above. 82 | * `@RunWith(…)` tells JUnit to give Spring Framework the control over the test execution. 83 | 84 | [[spring.application-components]] 85 | == Application components 86 | 87 | Application components are usually identified by an annotation that is either `@Component` or an annotation which is annotated with `@Component` in turn (e.g. `@Service`, `@Repository`, `@Controller`). 88 | The component classes are discovered at bootstrap time and a single instance is created. 89 | + 90 | .A simple application component 91 | ==== 92 | [source, java] 93 | ---- 94 | @Component 95 | class MyApplicationComponent {} 96 | ---- 97 | ==== 98 | 99 | If a component needs other components to work with (e.g. the web controller needs access to the component implementing data access), the component required can be injected into the depending component by declaring a constructor taking a dependency of the necessary type. 100 | 101 | .A simple component with a dependency 102 | ==== 103 | [source, java] 104 | ---- 105 | @Component 106 | class MyDependingComponent { 107 | 108 | private final MyApplicationComponent dependency; 109 | 110 | public MyDependingComponent(MyApplicationComponent dependency) { 111 | this.dependency = dependency; 112 | } 113 | } 114 | ---- 115 | ==== 116 | 117 | If a component depended on cannot be found in the container, an exception is thrown: 118 | 119 | .A Spring exception indicating a component cannot be found 120 | ==== 121 | ---- 122 | Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [demo.MyApplicationComponent] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {} 123 | at o.s.b.f.s.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(…:1118) 124 | at o.s.b.f.s.DefaultListableBeanFactory.doResolveDependency(…:967) 125 | at o.s.b.f.s.DefaultListableBeanFactory.resolveDependency(…:862) 126 | at o.s.b.f.s.ConstructorResolver.resolveAutowiredArgument(…:811) 127 | at o.s.b.f.s.ConstructorResolver.createArgumentArray(…:739) 128 | ... 42 common frames omitted 129 | ---- 130 | ==== 131 | 132 | [NOTE] 133 | ==== 134 | When using STS, classes that are Spring components carry a little S-overlay on the icon: 135 | 136 | image::sts-components.png[] 137 | ==== 138 | -------------------------------------------------------------------------------- /spring-webapps/README.adoc: -------------------------------------------------------------------------------- 1 | # Web application development with Java and Spring 2 | 3 | This repository contains the script for a lecture I held at TU Dresden in support of the Sofware Engineering Lab in winter semester 2016. It's primary goal is to ramp up the students with the very basics of Java web application development. 4 | 5 | The script is written in http://www.asciidoctor.org[Asciidoctor] and can be viewed http://static.olivergierke.de/lectures/spring-webapps/index.html[here]. To build the HTML file yourself, install Asciidoctor as described http://asciidoctor.org/docs/install-toolchain/[in the documentation] and run `asciidoctor index.adoc`. 6 | -------------------------------------------------------------------------------- /spring-webapps/code/.gitignore: -------------------------------------------------------------------------------- 1 | .mvn 2 | .springBeans 3 | -------------------------------------------------------------------------------- /spring-webapps/code/complete/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | example-complete 7 | 8 | Spring Webapps - Example - Complete 9 | 10 | 11 | de.odrotbohm.lectures.web 12 | example 13 | 0.0.1-SNAPSHOT 14 | ../../pom.xml 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /spring-webapps/code/complete/src/main/java/example/Application.java: -------------------------------------------------------------------------------- 1 | package example; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(Application.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /spring-webapps/code/complete/src/main/java/example/Greeter.java: -------------------------------------------------------------------------------- 1 | package example; 2 | 3 | import org.springframework.stereotype.Component; 4 | import org.springframework.util.Assert; 5 | 6 | @Component 7 | class Greeter { 8 | 9 | String greet(String input) { 10 | 11 | Assert.notNull(input, "Input must not be null!"); 12 | 13 | return input.equals("TU Dresden") ? input.concat(", yay") : input; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /spring-webapps/code/complete/src/main/java/example/MyController.java: -------------------------------------------------------------------------------- 1 | package example; 2 | 3 | import java.util.Optional; 4 | 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.ui.Model; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestParam; 9 | 10 | @Controller 11 | class MyController { 12 | 13 | private final Greeter greeter; 14 | 15 | public MyController(Greeter greeter) { 16 | this.greeter = greeter; 17 | } 18 | 19 | @GetMapping("/hello") 20 | String myMethod(@RequestParam Optional name, Model model) { 21 | 22 | var whoToGreet = name.map(it -> greeter.greet(it)).orElse("World"); 23 | 24 | model.addAttribute("name", whoToGreet); 25 | return "hello"; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /spring-webapps/code/complete/src/main/resources/.gitignore: -------------------------------------------------------------------------------- 1 | !*.html 2 | -------------------------------------------------------------------------------- /spring-webapps/code/complete/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/spring-webapps/code/complete/src/main/resources/application.properties -------------------------------------------------------------------------------- /spring-webapps/code/complete/src/main/resources/templates/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello! 4 | 5 | 6 |

Hello!

7 | 8 | -------------------------------------------------------------------------------- /spring-webapps/code/complete/src/test/java/example/GreeterUnitTests.java: -------------------------------------------------------------------------------- 1 | package example; 2 | 3 | import static org.assertj.core.api.Assertions.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | public class GreeterUnitTests { 8 | 9 | @Test 10 | public void augmentsYayToTuDresden() { 11 | 12 | var greeter = new Greeter(); 13 | 14 | assertThat(greeter.greet("TU Dresden")).isEqualTo("TU Dresden, yay"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /spring-webapps/code/initial/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | example-initial 7 | 8 | Spring Webapps - Example - Initial 9 | 10 | 11 | de.odrotbohm.lectures.web 12 | example 13 | 0.0.1-SNAPSHOT 14 | ../../pom.xml 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /spring-webapps/code/initial/src/main/java/example/Application.java: -------------------------------------------------------------------------------- 1 | package example; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(Application.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /spring-webapps/code/initial/src/main/resources/.gitignore: -------------------------------------------------------------------------------- 1 | !*.html 2 | -------------------------------------------------------------------------------- /spring-webapps/code/initial/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/spring-webapps/code/initial/src/main/resources/application.properties -------------------------------------------------------------------------------- /spring-webapps/code/initial/src/test/java/example/package-info.java: -------------------------------------------------------------------------------- 1 | package example; 2 | -------------------------------------------------------------------------------- /spring-webapps/code/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | fi 118 | 119 | if [ -z "$JAVA_HOME" ]; then 120 | javaExecutable="`which javac`" 121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 122 | # readlink(1) is not available as standard on Solaris 10. 123 | readLink=`which readlink` 124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 125 | if $darwin ; then 126 | javaHome="`dirname \"$javaExecutable\"`" 127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 128 | else 129 | javaExecutable="`readlink -f \"$javaExecutable\"`" 130 | fi 131 | javaHome="`dirname \"$javaExecutable\"`" 132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 133 | JAVA_HOME="$javaHome" 134 | export JAVA_HOME 135 | fi 136 | fi 137 | fi 138 | 139 | if [ -z "$JAVACMD" ] ; then 140 | if [ -n "$JAVA_HOME" ] ; then 141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 142 | # IBM's JDK on AIX uses strange locations for the executables 143 | JAVACMD="$JAVA_HOME/jre/sh/java" 144 | else 145 | JAVACMD="$JAVA_HOME/bin/java" 146 | fi 147 | else 148 | JAVACMD="`which java`" 149 | fi 150 | fi 151 | 152 | if [ ! -x "$JAVACMD" ] ; then 153 | echo "Error: JAVA_HOME is not defined correctly." >&2 154 | echo " We cannot execute $JAVACMD" >&2 155 | exit 1 156 | fi 157 | 158 | if [ -z "$JAVA_HOME" ] ; then 159 | echo "Warning: JAVA_HOME environment variable is not set." 160 | fi 161 | 162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 163 | 164 | # traverses directory structure from process work directory to filesystem root 165 | # first directory with .mvn subdirectory is considered project base directory 166 | find_maven_basedir() { 167 | 168 | if [ -z "$1" ] 169 | then 170 | echo "Path not specified to find_maven_basedir" 171 | return 1 172 | fi 173 | 174 | basedir="$1" 175 | wdir="$1" 176 | while [ "$wdir" != '/' ] ; do 177 | if [ -d "$wdir"/.mvn ] ; then 178 | basedir=$wdir 179 | break 180 | fi 181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 182 | if [ -d "${wdir}" ]; then 183 | wdir=`cd "$wdir/.."; pwd` 184 | fi 185 | # end of workaround 186 | done 187 | echo "${basedir}" 188 | } 189 | 190 | # concatenates all lines of a file 191 | concat_lines() { 192 | if [ -f "$1" ]; then 193 | echo "$(tr -s '\n' ' ' < "$1")" 194 | fi 195 | } 196 | 197 | BASE_DIR=`find_maven_basedir "$(pwd)"` 198 | if [ -z "$BASE_DIR" ]; then 199 | exit 1; 200 | fi 201 | 202 | ########################################################################################## 203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 204 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 205 | ########################################################################################## 206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 207 | if [ "$MVNW_VERBOSE" = true ]; then 208 | echo "Found .mvn/wrapper/maven-wrapper.jar" 209 | fi 210 | else 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 213 | fi 214 | if [ -n "$MVNW_REPOURL" ]; then 215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar" 216 | else 217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar" 218 | fi 219 | while IFS="=" read key value; do 220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 221 | esac 222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 223 | if [ "$MVNW_VERBOSE" = true ]; then 224 | echo "Downloading from: $jarUrl" 225 | fi 226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 227 | if $cygwin; then 228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 229 | fi 230 | 231 | if command -v wget > /dev/null; then 232 | if [ "$MVNW_VERBOSE" = true ]; then 233 | echo "Found wget ... using wget" 234 | fi 235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 236 | wget "$jarUrl" -O "$wrapperJarPath" 237 | else 238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" 239 | fi 240 | elif command -v curl > /dev/null; then 241 | if [ "$MVNW_VERBOSE" = true ]; then 242 | echo "Found curl ... using curl" 243 | fi 244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 245 | curl -o "$wrapperJarPath" "$jarUrl" -f 246 | else 247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 248 | fi 249 | 250 | else 251 | if [ "$MVNW_VERBOSE" = true ]; then 252 | echo "Falling back to using Java to download" 253 | fi 254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 255 | # For Cygwin, switch paths to Windows format before running javac 256 | if $cygwin; then 257 | javaClass=`cygpath --path --windows "$javaClass"` 258 | fi 259 | if [ -e "$javaClass" ]; then 260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 261 | if [ "$MVNW_VERBOSE" = true ]; then 262 | echo " - Compiling MavenWrapperDownloader.java ..." 263 | fi 264 | # Compiling the Java class 265 | ("$JAVA_HOME/bin/javac" "$javaClass") 266 | fi 267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 268 | # Running the downloader 269 | if [ "$MVNW_VERBOSE" = true ]; then 270 | echo " - Running MavenWrapperDownloader.java ..." 271 | fi 272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 273 | fi 274 | fi 275 | fi 276 | fi 277 | ########################################################################################## 278 | # End of extension 279 | ########################################################################################## 280 | 281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 282 | if [ "$MVNW_VERBOSE" = true ]; then 283 | echo $MAVEN_PROJECTBASEDIR 284 | fi 285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 286 | 287 | # For Cygwin, switch paths to Windows format before running java 288 | if $cygwin; then 289 | [ -n "$M2_HOME" ] && 290 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 291 | [ -n "$JAVA_HOME" ] && 292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 293 | [ -n "$CLASSPATH" ] && 294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 295 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 297 | fi 298 | 299 | # Provide a "standardized" way to retrieve the CLI args that will 300 | # work with both Windows and non-Windows executions. 301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 302 | export MAVEN_CMD_LINE_ARGS 303 | 304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 305 | 306 | exec "$JAVACMD" \ 307 | $MAVEN_OPTS \ 308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 311 | -------------------------------------------------------------------------------- /spring-webapps/code/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /spring-webapps/code/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | de.odrotbohm.lectures.web 7 | example 8 | 0.0.1-SNAPSHOT 9 | pom 10 | 11 | Spring Webapps - Example 12 | Sample project for how to build web applications with Spring MVC 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.3.4.RELEASE 18 | 19 | 20 | 21 | initial 22 | complete 23 | 24 | 25 | 26 | 11 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-web 36 | 37 | 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-thymeleaf 43 | 44 | 45 | 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-starter-test 50 | test 51 | 52 | 53 | org.junit.vintage 54 | junit-vintage-engine 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-devtools 64 | 65 | 66 | 67 | 68 | 69 | 70 | verify 71 | 72 | 73 | 74 | org.springframework.boot 75 | spring-boot-maven-plugin 76 | 77 | 78 | org.springframework.boot.experimental 79 | spring-boot-thin-layout 80 | 1.0.25.RELEASE 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /spring-webapps/images/images.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/spring-webapps/images/images.key -------------------------------------------------------------------------------- /spring-webapps/images/lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odrotbohm/lectures/522f479fdf632ae845587e6935beb93814387f53/spring-webapps/images/lifecycle.png -------------------------------------------------------------------------------- /spring-webapps/index.adoc: -------------------------------------------------------------------------------- 1 | = Web application development with Java and Spring 2 | Oliver Drotbohm 3 | :revdate: {docdatetime} 4 | :revremark: This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 5 | :numbered: 6 | :experimental: 7 | :sectids!: 8 | :sectanchors: true 9 | :icons: font 10 | :toc: left 11 | :imagesdir: images 12 | 13 | This documents summarizes the content of a lecture I held at TU Dresden in support of the Software Engineering Lab. 14 | Its primary goal is to ramp up the students with the very basics of Java web application development. 15 | If you're looking for the script used in years 2014 and 2015, it's been relocated to link:../../lectures/spring-webapps-old/[here]. 16 | 17 | :numbered!: 18 | [preface] 19 | [[introduction]] 20 | == Introduction 21 | 22 | The fundamental goals of the lecture are: 23 | 24 | * Learn how a web application works fundamentally 25 | * Learn how you'd implement a web application using Spring MVC 26 | 27 | The lecture content is three-fold: we're going to start by looking at how a web application works fundamentally. 28 | Having identified the most crucial aspects of that, we're going to explore how to use Spring MVC to implement those aspects using Spring MVC. 29 | We're then going to have a look at how to integrate business code with the code we wrote for the web handling. 30 | 31 | Once that's done we're exploring what the Guestbook sample applicationfootnote:[Guestbook sample application -- https://github.com/st-tu-dresden/guestbook[GitHub repository]] consists of. 32 | We're then going to cover aspects of Spring Data JPA to implement persistence and Spring MVC for the web layer. 33 | 34 | The source document for this script can be found in https://github.com/odrotbohm/lectures[this GitHub repository]. Feel free to create tickets for improvements or provide patches via pull requests. 35 | 36 | [[introduction.prerequisites]] 37 | === Prerequisites 38 | 39 | This script assumes you've worked through the scripts of the summer semester: 40 | 41 | 1. link:../../lectures/java-tooling/[Fundamentals of Java Tooling] -- will ramp you up on the most fundamental tools you're gonna be working with when developing Java applications: IDEs, build tools etc. 42 | 2. link:../../lectures/frameworks-and-libraries/[Frameworks & Libraries] -- will ramp you up on the topic of how to work with libraries and frameworks with Spring as application framework in particular. 43 | 3. link:../../lectures/ddd-and-spring/[Domain-Driven Design & Spring] -- will ramp you up on the fundamental concepts of Domain-Driven Design and how those concepts are reflected in Spring Framework applications. 44 | 4. link:../../lectures/collaborative-sd/[Collaborative Software Development] (optional) -- will ramp you up on tools and methodologies used when writing software in a team. 45 | 46 | :numbered: 47 | 48 | [[understanding-the-web]] 49 | == Fundamentals of the web 50 | 51 | Objective: understanding the building blocks of the web to a degree that it allows you to understand how a browser interacts with a server. 52 | 53 | image::lifecycle.png[] 54 | 55 | * Biggest distributed network ever built (REST -> Roy Fielding's https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm[dissertation]) 56 | * Browser -> Request (Header, Body)-> Server 57 | * Server -> Response (typically HTML, JSON) -> Clients interprets response 58 | * Resources, URI, uniform interface (HTTP methods) 59 | 60 | [[handling-web-requests]] 61 | == Handling web requests with Spring MVC 62 | 63 | * MVC in the web: Model, View, Controller 64 | * Model: data to be presented, a certain view on the domain model of the server. 65 | * View: a template to describe the representation (usually using some mechanism for dynamic composition) 66 | * Controller: a piece of code that translates HTTP requests into the calls to the actual business logic 67 | 68 | [[handling-web-requests.request]] 69 | === Mapping a request 70 | 71 | First thing we have to take care of is that a web request actually triggers some of our code is actually invoked. 72 | To achieve that Spring MVC provides a variety of annotations to allow us to define for which requests a method should be called. 73 | 74 | [title="Mapping a request"] 75 | ==== 76 | [source, java, title="`MyController.java` in package `example` under `src/main/java`"] 77 | ---- 78 | @Controller <1> 79 | class MyController { 80 | 81 | @GetMapping("/hello") <2> 82 | void myMethod(@RequestParam Optional name) {} <3> 83 | } 84 | ---- 85 | <1> As the name suggests , the `@Controller` annotation declares the class to be a controller. 86 | This will cause Spring MVC to create an instance of that class and inspect it for request mapping annotations so that it can route incoming requests to them. 87 | <2> Maps the method to a particular request. I.e. if a request is matching the configuration, the method will be called. 88 | <3> Annotated parameters to access parts of the request, in this case a request parameter named `name`. 89 | ==== 90 | 91 | Declaring a class like just seen will cause our method to be called for `GET` requests to `/hello`. 92 | The method will receive the `name` request parameter wrapped into an `Optional` if the parameter was present in the request. 93 | If no parameter named `name` was present, the method will receive `Optional.empty()`. 94 | 95 | Next, we'll have to create a response to answer that request. 96 | 97 | [[handling-web-requests.response]] 98 | === Creating a response 99 | 100 | Again the framework is helping us here by doing all the heavy lifting for us. 101 | In an MVC context, a response is usually created by using a so called template, which -- as we'd like to take a simple step here -- can be a static HTML page. 102 | 103 | [title="The controller selecting a view"] 104 | ==== 105 | [source, java] 106 | ---- 107 | @Controller 108 | class MyController { 109 | 110 | @GetMapping("/hello") 111 | String myMethod(@RequestParam Optional name) { <1> 112 | return "hello"; <2> 113 | } 114 | } 115 | ---- 116 | [source,html, title="`template/hello.html` in `src/main/resources`"] 117 | ---- 118 | 119 | 120 | Hello! 121 | 122 | 123 |

Hello!

124 | 125 | 126 | ---- 127 | <1> The method signature has been changed to return a `String` now. 128 | <2> The method returns a logical name to select a view. The simple `hello` will be translated into a lookup of a file under `templates/hello.html` (usually placed in `src/main/resources`). 129 | ==== 130 | 131 | [[handling-web-requests.model]] 132 | === Using a model 133 | 134 | Let's say we'd want refer to the name provided as request parameter from within the view template. 135 | The "communication" between controller and view is achieved using a model. 136 | Spring MVC provides a programmatic abstraction for that which can be accessed by just declaring `Model` as controller method parameter. 137 | 138 | [title="Populating the model"] 139 | ==== 140 | [source, java] 141 | ---- 142 | @Controller 143 | class MyController { 144 | 145 | @GetMapping("/hello") 146 | String myMethod(@RequestParam Optional name, Model model) { <1> 147 | 148 | model.addAttribute("name", name.orElse("World")); <2> 149 | 150 | return "hello"; 151 | } 152 | } 153 | ---- 154 | <1> Framework provides access to the model if we declare a corresponding method parameter. 155 | <2> We can then populate the model by adding an attribute named `name` to it. 156 | As the request parameter can be absent, we default it to `World` here. 157 | ==== 158 | 159 | NOTE: Find an overview about all supported annotations https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-methods[here] and http://engineering.pivotal.io/post/must-know-spring-boot-annotations-controllers/[here]. 160 | 161 | [source,html] 162 | ---- 163 | <1> 164 | 165 | Hello! 166 | 167 | 168 |

Hello!

<2> 169 | 170 | 171 | ---- 172 | <1> We register an XML namespace for Thymeleaf, a template engine that allows us to enrich our previously static template with dynamic parts. 173 | All elements and attributes from the `th`-namespace will be processed by the template engine and replace the content of the elements that carry the attributes. 174 | 175 | [[handling-web-requests.business-logic]] 176 | === Invoking business logic 177 | 178 | * Architecture: web layer -> business layer -> persistence 179 | 180 | Let's make this practical. Assume we'd want to treat one particular name in a special way. 181 | A naïve approach might be to implement that particular logic directly in the controller. 182 | 183 | [title="Adding business logic"] 184 | ==== 185 | [source, java] 186 | ---- 187 | @Controller 188 | class MyController { 189 | 190 | @GetMapping("/hello") 191 | String myMethod(@RequestParam Optional name, Model model) { 192 | 193 | var whoToGreet = name 194 | .map(it -> it.equals("TU Dresden") ? it.concat(", yay") : it) <1> 195 | .orElse("World"); 196 | 197 | model.addAttribute("name", whoToGreet); 198 | 199 | return "hello"; 200 | } 201 | } 202 | ---- 203 | <1> We treat `TU Dresden` given as name by effectively turning it into a `TU Dresden, yay` 204 | ==== 205 | 206 | What's the problem with this? 207 | We're starting to intermingle different concerns here: we're mixing request handling (mapping the request, accessing the request parameter) and response preparing logic (populating the model) with the actual business logic. 208 | But why is this a problem? 209 | The more concerns we mix together, the harder it will become to actually test certain aspects of the application. 210 | Assume, we only wanted to test that `TU Dresden` gets translated into `TU Dresden, yay`. 211 | The test code would have to look something like this: 212 | 213 | [title="A unit test for a Spring MVC controller"] 214 | ==== 215 | [source, java, title="`MyControllerUnitTest.java` in package `example` under `src/test/java`"] 216 | ---- 217 | import static org.assertj.core.api.Assertions.*; 218 | 219 | class MyControllerUnitTests { 220 | 221 | @Test 222 | void augmentsYayToTuDresden() { 223 | 224 | var controller = new MyController(); 225 | 226 | var model = new ExtendedModelMap(); 227 | controller.myMethod(Optional.of("TU Dresden"), model); 228 | 229 | assertThat(model.asMap().get("name")).isEqualTo("TU Dresden, yay"); 230 | } 231 | } 232 | ---- 233 | ==== 234 | 235 | We basically have to mimic the frameworks behavior here and can only test the actual business logic in an indirect way. 236 | We have to prepare a `Model` instance, which requires knowledge about the framework. 237 | We invoke the method just like the framework. 238 | And finally, we have to know about the key that we use to populate the model. 239 | Sure we could use a constant here but let's try something different and see how this affects both the controller implementation and the test code. 240 | 241 | Let's extract the actual business logic into a separate class: 242 | 243 | [title="Extracting business logic into a dedicated class"] 244 | ==== 245 | [source, java, title="`Greeter.java` in package `example` under `src/main/java`"] 246 | ---- 247 | class Greeter { 248 | 249 | String greet(String input) { 250 | 251 | Assert.hasText(input, "Input must not be null or empty!"); 252 | 253 | return input.equals("TU Dresden") ? input.concat(", yay") : input; 254 | } 255 | } 256 | ---- 257 | ==== 258 | 259 | This piece of code is way more precise as it leaves all web related artifacts away. 260 | It's pure business logic: verify the input actually has text and perform our business logic on it. 261 | So, what would a test for the business logic implemented like this look like? 262 | 263 | [title="A unit test for the business logic"] 264 | ==== 265 | [source, java, title="`GreeterUnitTests.java` in package `example` under `src/test/java`"] 266 | ---- 267 | class GreeterUnitTests { 268 | 269 | @Test 270 | void augmentsYayToTuDresden() { 271 | 272 | var greeter = new Greeter(); 273 | 274 | assertThat(greeter.greet("TU Dresden")).isEqualTo("TU Dresden, yay"); 275 | } 276 | } 277 | ---- 278 | ==== 279 | 280 | Note, how the code has become significantly simpler as we don't have to deal with the framework APIs at all. 281 | 282 | How do we actually integrate the functionality extracted into the `Greeter` class into the controller. 283 | If we want to call the method on `Greeter`, we need to make sure the controller gets an instance of it. 284 | That means, the controller has a dependency on `Greeter` -- the former cannot work without the latter. 285 | A dependency of a class is expressed by creating a constructor taking the dependency as argument. 286 | We keep the reference around in a field, so that we can use it in the method. 287 | 288 | [title="Using the `Greeter` in the controller"] 289 | ==== 290 | [source, java] 291 | ---- 292 | @Controller 293 | class MyController { 294 | 295 | private final Greeter greeter; <1> 296 | 297 | MyController(Greeter greeter) { <2> 298 | this.greeter = greeter; 299 | } 300 | 301 | @GetMapping("/hello") 302 | String myMethod(@RequestParam Optional name, Model model) { 303 | 304 | var whoToGreet = name.map(it -> greeter.greet(it)).orElse("World"); <3> 305 | 306 | model.addAttribute("name", whoToGreet); <4> 307 | 308 | return "hello"; 309 | } 310 | } 311 | ---- 312 | <1> A field to keep the dependency around and to be able to refer to it from within the method. 313 | <2> A constructor to express the dependency. 314 | <3> Using the `Greeter` to access the business logic implemented in it. 315 | <4> Add result to the model. 316 | ==== 317 | 318 | If we try to run this piece of code, the application will fail to start and express it cannot create an instance of `MyController` as it doesn't know about a `Greeter`. 319 | 320 | [source] 321 | ---- 322 | 2016-10-18 12:48:21.468 ERROR 3188 --- [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter : 323 | 324 | *************************** 325 | APPLICATION FAILED TO START 326 | *************************** 327 | 328 | Description: 329 | 330 | Parameter 0 of constructor in example.MyController required a bean of type 'example.Greeter' that could not be found. 331 | 332 | 333 | Action: 334 | 335 | Consider defining a bean of type 'example.Greeter' in your configuration. 336 | ---- 337 | 338 | Why is that? 339 | Remember that Spring will create instances of the classes it knows about? 340 | It knows about the controller class, as it is annotated with `@Controller`. 341 | It discovers the constructor and realizes, it needs a `Greeter` first. 342 | However, `Greeter` isn't under Spring's control yet as we haven't expressed that it's supposed to be managed by Spring yet. 343 | We can fix this by annotating with `@Component`. 344 | 345 | [title="Tuning `Greeter` into a Spring bean"] 346 | ==== 347 | [source, java, title="Greeter.java"] 348 | ---- 349 | @Component <1> 350 | class Greeter { 351 | 352 | String greet(String input) { 353 | 354 | Assert.hasText(input, "Input must not be null or empty!"); 355 | 356 | return input.equals("TU Dresden") ? input.concat(", yay") : input; 357 | } 358 | } 359 | ---- 360 | <1> The `@Component` annotation will make the class known to Spring and available for injection into other Spring beans. 361 | ==== 362 | During the next bootstrap, the following will happen: 363 | 364 | 1. Spring scans your application and it will find both `MyController` and `Greeter` as they're both annotated. 365 | 2. Spring will discover that -- for a `MyComponent` instance to be created -- it needs a `Greeter` instance first. 366 | 3. Spring creates the `Greeter` instance. 367 | 4. Spring creates the `MyComponent` instance by handing the just created `Greeter` instance to the constructor. 368 | 369 | Effectively, what Spring does is roughly equivalent to this snippet of code: 370 | 371 | [source, java] 372 | ---- 373 | var greeter = new Greeter(); 374 | var controller = new MyController(greeter); 375 | ---- 376 | 377 | The concept of exposing a required dependency as constructor parameter and some code (framework or manually written) handing in instances of those dependencies is called Inversion of Control. 378 | Read more about that in link:../../lectures/frameworks-and-libraries/index.html#spring.ioc[Inversion of Control]. 379 | 380 | :numbered!: 381 | == Appendix 382 | 383 | [appendix] 384 | === Recommended guides 385 | 386 | * https://spring.io/guides/gs/serving-web-content/[Serving web content with Spring MVC] 387 | * https://spring.io/guides/gs/accessing-data-jpa/[Accessing relational data with JPA] 388 | * https://spring.io/guides/gs/securing-web/[Securing a web application] 389 | 390 | [appendix] 391 | === Resources 392 | 393 | * link:../../lectures/frameworks-and-libraries/[Script: Frameworks and Libraries] 394 | * link:../../lectures/ddd-and-spring/[Script: DDD & Spring] 395 | * https://github.com/st-tu-dresden/guestbook[The Guestbook sample application] 396 | * http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle[Spring Boot reference documentation] 397 | * http://docs.spring.io/spring-framework/docs/current/spring-framework-reference[Spring Framework reference documentation] 398 | * http://docs.spring.io/spring-data/jpa/docs/current/reference/html[Spring Data JPA reference documentation] 399 | * https://spring.io/guides[Getting started guides] 400 | * http://projects.spring.io/spring-boot[Spring Boot project home] 401 | 402 | [appendix] 403 | === License 404 | image::https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png[link="http://creativecommons.org/licenses/by-nc-sa/4.0/"] 405 | -------------------------------------------------------------------------------- /spring/.gitignore: -------------------------------------------------------------------------------- 1 | images/*.svg 2 | -------------------------------------------------------------------------------- /spring/index.adoc: -------------------------------------------------------------------------------- 1 | = Spring – A Java Application Framework 2 | Oliver Drotbohm 3 | :doctype: book 4 | :revdate: {docdatetime} 5 | :revremark: This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 6 | :numbered: 7 | :sectnumlevels: 2 8 | :experimental: 9 | :source-highlighter: highlightjs 10 | :highlightjsdir: ../js/highlight 11 | :highlightjs-theme: atom-one-dark-reasonable 12 | :sectids!: 13 | :sectanchors: true 14 | :icons: font 15 | :toc: left 16 | :toclevels: 3 17 | :livebase: http://static.odrotbohm.de/lectures 18 | :imagesdir: images 19 | :plantuml-format: svg 20 | 21 | [abstract] 22 | :numbered!: 23 | = Abstract 24 | Frameworks are ubiquitous in todays software development world. 25 | They help developers to focus on implementing business code and and abstract and implement technical aspects aspects of programming. 26 | They effectively form an instance of the concept of practical reuse in software engineering. 27 | 28 | A major challenge in developing and using frameworks is the coupling introduced between the software component being developed and the framework, whose development is usually not controlled by the entity developing software with it. 29 | 30 | The slides to present this script can be found link:spring-a-java-application-framework.pdf[here]. 31 | 32 | :numbered: 33 | [[intro]] 34 | == Introduction 35 | 36 | Spring Frameworkfootnote:spring[Spring Framework – https://spring.io[Project website]] is a Java application framework started in the early 2000s. 37 | Initially a bit of sample code of a book by Rod Johnsonfootnote:rod-johnson[Rod Johnson – https://en.wikipedia.org/wiki/Rod_Johnson_(programmer)[Wikipedia page]], it has become the most widely used framework in enterprise Java applications worldwide in retail, banks, insurance, automobile and entertainment industriesfootnote:netflix-spring[Netflix OSS and Spring Boot coming full circle – https://medium.com/netflix-techblog/netflix-oss-and-spring-boot-coming-full-circle-4855947713a0[Blog]]. 38 | 39 | Since then it has grown from a already pretty sophisticated dependency injection container into an eco-system of frameworks that cover a broad set of use cases in application development. 40 | 41 | This script will give an overview about a Spring based web application and derive general traits of frameworks and the effects they have on application development. 42 | For a general comparison between libraries and frameworks as well as a step by step introduction into a Spring based web application see the link:{livebase}/frameworks-and-libraries/[Frameworks & Libraries] script. 43 | 44 | 45 | [[intro.sample]] 46 | === The Guestbook sample project 47 | 48 | The https://github.com/st-tu-dresden/guestbook[Guestbook sample project] is an as simple as possible technology showcase for a Spring based web application originally developed for my work with the TU Dresden to showcase the following technical aspects: 49 | 50 | * A Java and Maven based web application following the MVC pattern. 51 | * A minimal domain model following Domain-Driven Designfootnote:ddd[Domain-Driven Design – https://en.wikipedia.org/wiki/Domain-driven_design[Wikipedia page]] (see `GuestbookEntry`, `GuestbookEntryRepository`). 52 | * Persistence of the domain model into a relational database. 53 | * Template based, server side view rendering with JavaScript based optimizations (progressive enhancement). 54 | * Secured access to partial, administrative functionality. 55 | * General structure of a web based application as far as this simple example allows. 56 | 57 | The parts of the Spring Framework ecosystem that are involved are the following 58 | 59 | * Spring Boot – for general application configuration, conventions and defaulting. 60 | * Spring Data JPA – for relational database access via the repository abstraction. 61 | * Spring Security – to implement secured access to parts of the functionality. 62 | 63 | [[frameworks]] 64 | == Frameworks 65 | 66 | [[frameworks.goals]] 67 | === Goals of frameworks 68 | 69 | . __Separation of concernsfootnote:soc[Sepearation of Soncerns – https://en.wikipedia.org/wiki/Separation_of_concerns[Wikipedia page]]__ -- Developers should be able to concentrate on modeling and implementing the business domain. 70 | I.e. we want to separate technical aspects of the implementation (database access etc.) from the ones of the business logic and reduce the amount of technical code to be written in the first place. 71 | . __Raising the abstraction level__ -- Code should be written in a way that within a class or component it's based on the same level of abstraction as otherwise it's hard to understand and reason about. 72 | . __Remove boilerplate code__ -- Developers usually don't appreciate to have to code a lot of repeated code to achieve simple tasks. 73 | 74 | 75 | [[spring]] 76 | == Spring Framework 77 | 78 | [[spring.di]] 79 | === Dependency Injection 80 | 81 | Dependency Injection (DI) footnote:di[Dependency Injection – https://en.wikipedia.org/wiki/Dependency_injection[Wikipedia page]]footnote:spring-ioc[Inversion of Control – {livebase}/frameworks-and-libraries/#spring.ioc[Script "Frameworks & Libraries"]] is the idea that an object does not assemble its collaborators itself but exposes which ones it needs to work, usually via constructor arguments. 82 | A third party – manually written code or a so called DI container – then assembles the net of objects at application startup. 83 | The approach implements the Separation of Concernsfootnote:soc[] paradigm by separating the phases of object construction and usage and with that usually frees the objects from infrastructure concerns so that it can concentrate on implementing business logic. 84 | 85 | .An application component depending on another 86 | [source, java] 87 | ---- 88 | class MyService { 89 | 90 | private final MyRepository repository; 91 | 92 | MyService(MyRepository repository) { <1> 93 | this.repository = repository; 94 | } 95 | 96 | void someBusinessMethod(…) { 97 | this.repository.save(new MyEntity()); 98 | } 99 | } 100 | 101 | interface MyRepository { 102 | MyEntity save(MyEntity entity); 103 | } 104 | 105 | class JpaRepository implements MyRepository { 106 | 107 | @Override 108 | MyEntity save(MyEntity entity) { … } 109 | } 110 | 111 | // Manual dependency injection 112 | MyRepository repository = new MyRepository(…); 113 | MyService service = new MyService(repository); <2> 114 | ---- 115 | <1> `MyService` expresses a dependency to `MyRepository` instead of creating it itself. 116 | <2> The manual setup code now hands an instance of `MyRepository` to the service instance to be created. 117 | 118 | .Logical structure of application components 119 | [plantuml, components] 120 | ---- 121 | class MyService { 122 | -repository : MyRepository 123 | +someBusinessMethod() : void 124 | } 125 | 126 | interface MyRepository { 127 | MyEntity save(entity : MyEntity) 128 | } 129 | 130 | class JpaRepository { 131 | MyEntity save(entity : MyEntity) 132 | } 133 | 134 | MyService -right-> MyRepository 135 | JpaRepository .left.|> MyRepository 136 | ---- 137 | 138 | [[spring.di.why]] 139 | ==== Why use a framework? 140 | 141 | First and foremost, using a Dependency Injection framework has the benefit of removing the need to manually code the wiring of objects, i.e. it saves boilerplate code (see <>). 142 | It also enables the framework to wrap the dependency into a decorating adapter, to apply technical services to the managed code (see <>). 143 | 144 | .Spring managed application components 145 | [source, java] 146 | ---- 147 | package com.acme; 148 | 149 | @Component <1> 150 | class MyService { … } 151 | 152 | @Component <1> 153 | class JpaRepository { … } 154 | 155 | // Spring-driven dependency injection 156 | ApplicationContext context = new AnnotationConfigApplicationContext("com.acme"); <2> 157 | MyService service = context.getBean(MyService.class); <3> 158 | ---- 159 | <1> Classes are annotated with framework specific annotations so that it can discover the components of an application that it's supposed to handle. 160 | <2> The detection is triggered by bootstrapping the framework pointing it to the code written by the user. 161 | <3> Framework API is then used to access the components. Note, that usually this last step can be avoided completely as the framework bootstraps e.g. a web component that will map incoming requests to user components (see <>). 162 | 163 | [[spring.psa]] 164 | === Portable Services Abstraction 165 | 166 | Spring Framework helps implementing technical aspects of the application like security and transactions in a declarative way by providing annotations to capture settings for those aspects. 167 | When creating the object tree, it then decorates the instance 168 | 169 | [[spring.psa.security-transactions]] 170 | ==== Security & Transactions 171 | 172 | .Spring components using services like security and transactions 173 | [source, java] 174 | ---- 175 | @Component 176 | class MyService { 177 | 178 | private final MyRepository repository; 179 | 180 | … 181 | 182 | @PreAuthorize("hasRole('ADMIN')") <1> 183 | void someBusinessMethod(…) { 184 | this.repository.save(new MyEntity()); 185 | } 186 | } 187 | 188 | @Component 189 | class JpaRepository implements MyRepository { 190 | 191 | @Transactional <2> 192 | MyEntity save(MyEntity entity) { … } 193 | } 194 | ---- 195 | <1> Express that `someBusinessMethod(…)` is supposed to be only used by administrators. 196 | <2> Expresses that all database operations happening within `save(…)` are supposed to be transactional, i.e. atomic. 197 | 198 | Note, how we don't have to write code ourselves that makes all this happen. 199 | We just declare what we want to happen and the framework takes care of actually implementing it. 200 | It's also worth mentioning that the annotations are the only Spring dependencies of the code, i.e. there's only a very thin connection between user code and the framework. 201 | 202 | [[spring.psa.implementation]] 203 | ==== Implementation details 204 | 205 | The application of the technical services is implemented using the proxy patternfootnote:proxy[Proxy pattern – https://en.wikipedia.org/wiki/Proxy_pattern[Wikipedia page]] in which a proxy for the target component is generated at runtime. A chain of interceptorsfootnote:interceptor[Interceptor pattern – https://en.wikipedia.org/wiki/Interceptor_pattern[Wikipedia page]] is computed at bootstrap time and handles the technical concerns (in this particular case the management of a transaction). 206 | The framework creates an instance of the target component enriched with additional functionality and injects that into the client component. 207 | This can only be achieved as the client component avoids an active lookup or creation of the collaborating component itself. 208 | 209 | .Type and object structure at runtime 210 | [plantuml, runtime] 211 | ---- 212 | class MyService <> #lightblue { 213 | -repository : MyRepository 214 | } 215 | 216 | interface MyRepository #lightblue 217 | 218 | class MyRepositoryProxy { 219 | -delegate : JpaRepository 220 | } 221 | note left: Provided by Spring\n at runtime 222 | 223 | class JpaRepository <> #lightblue implements MyRepository 224 | 225 | MyService .down.> MyRepository 226 | MyService --> MyRepositoryProxy 227 | MyRepositoryProxy .up.|> MyRepository 228 | MyRepositoryProxy .right.> JpaRepository 229 | ---- 230 | Note, how `MyRepositoryProxy` implements `MyRepository` so that it can act as a replacement for the user provided `JpaRepository` within `MyService`. 231 | 232 | The actual implementation is created by Spring Framework's `ProxyFactory` that makes it easy to decorate target instances with generic `MethodInterceptor` implementations that use of Aspect-oriented Programming (AOP)footnote:aop[Aspect-oriented programming – https://en.wikipedia.org/wiki/Aspect-oriented_programming[Wikipedia page]] and reflectionfootnote:reflection[Reflection -- https://en.wikipedia.org/wiki/Reflection_(computer_programming)[Wikipedia page]] at runtime. 233 | 234 | .Invocation flow for on a proxied component 235 | [plantuml, proxy-interaction] 236 | ---- 237 | hide footbox 238 | 239 | participant MyService <> #lightblue 240 | participant MyRepositoryProxy 241 | 242 | [-> MyService: someBusinessMethod() 243 | MyService -> MyRepositoryProxy : save(…) 244 | activate MyRepositoryProxy 245 | 246 | box "Interceptor chain" 247 | participant TransactionInterceptor 248 | participant "…" 249 | end box 250 | 251 | MyRepositoryProxy -> TransactionInterceptor 252 | TransactionInterceptor -> "…" : invocation.proceed() 253 | participant JpaRepository <> #lightblue 254 | "…" -> JpaRepository 255 | "…" <- JpaRepository 256 | TransactionInterceptor <- "…" 257 | MyRepositoryProxy <- TransactionInterceptor 258 | MyService <- MyRepositoryProxy 259 | deactivate MyRepositoryProxy 260 | ---- 261 | 262 | .A simplified example of a generic method interceptor to handle transactions 263 | [source, java] 264 | ---- 265 | class TransactionInterceptor implements MethodInterceptor { 266 | 267 | @Override 268 | public Object invoke(MethodInvocation invocation) throws Throwable { 269 | 270 | // Start transaction 271 | Transaction transaction = … 272 | 273 | try { 274 | 275 | Object result = invocation.proceed(); <1> 276 | 277 | transaction.commit() 278 | 279 | return result; 280 | 281 | } catch (RuntimeException e) { 282 | transaction.rollback() 283 | throw e; 284 | } 285 | } 286 | } 287 | 288 | // Wrapping the application component into a transactional proxy 289 | JpaRepository repository = new JpaRepository(); 290 | ProxyFactory factory = new ProxyFactory(repository); 291 | factory.addAdvice(new TransactionInterceptor()); 292 | 293 | MyRepository proxy = factory.getProxy(); 294 | ---- 295 | <1> The call triggers all other interceptors in the chain and the invocation of the method on the final target instance eventually. 296 | 297 | [[spring.mvc]] 298 | === Spring MVC 299 | 300 | Spring MVC is a web application framework that implements the Model View Controller pattern. 301 | It allows to implement controller classes equipped with mapping annotations to map HTTP requests into method invocations and bind request parameters and payloads to method arguments. 302 | 303 | .A SpringMVC REST controller 304 | [source, java] 305 | ---- 306 | @RestController <1> 307 | class MyController { 308 | 309 | @GetMapping("/hello") <2> 310 | String sayHelloTo(@RequestParam Optional name) { <3> 311 | return String.format("Hello, %s!", name.orElse("world")); 312 | } 313 | } 314 | ---- 315 | <1> An annotation to make the component known to the framework and assign it a given role (here: a Spring WebMVC controller). 316 | <2> An annotation to map an incoming web request with the configured path to the annotated method. Method parameters can be used to access different parts of the requests like headers, request parameters etc. 317 | <3> An annotated parameter to express we want to get access to the request parameter named `name`. Wrapped into an `Optional` as the request might not include that parameter and we have to handle that case in the implementation. 318 | 319 | The above shown controller class causes the following HTTP requests handled like this: 320 | 321 | [source] 322 | ---- 323 | GET /hello -> Hello, world! 324 | GET /hello?name=BB8 -> Hello, BB8! 325 | ---- 326 | 327 | [[using-frameworks]] 328 | == Using Frameworks 329 | 330 | [[using-frameworks.reuse-vs-coupling]] 331 | === Reuse versus coupling 332 | 333 | Traditionally code reuse has been a topic of focus as it promised economics of scale: the code a team does not have to write, it can spend on business problems. 334 | The downside of this approach is that the client code has to opt into the design decisions of the serving code which creates coupling. 335 | 336 | .A client component using another component 337 | [plantuml, coupling-1] 338 | ---- 339 | [A] ..> [B] : use 340 | ---- 341 | 342 | Frameworks usually react to this challenge by exposing very generic concepts and means to configure the framework which makes it adaptable to different client's needs. 343 | However, this in turn means that there's the need to configure the framework which couples the clients to the framework. 344 | 345 | .An application configuring the framework 346 | [plantuml, coupling-2] 347 | ---- 348 | Configuration - [Framework] 349 | [Application] ..> Configuration 350 | ---- 351 | 352 | These challenges get elevated if a framework is used by a lot of clients as it has to balance all the different requirements that those have. 353 | 354 | .Multiple applications using the framework 355 | [plantuml, coupling-3] 356 | ---- 357 | [Application] ..> [Framework] 358 | [Application'] ..> [Framework] 359 | [Application''] ..> [Framework] 360 | [Application'''] ..> [Framework] 361 | ---- 362 | 363 | [[using-frameworks.java]] 364 | === Java and the enterprise 365 | 366 | Java is the most widely used programming language globallyfootnote:tiobe[TIOBE index – https://www.tiobe.com/tiobe-index/[Website]] and used in a lot of enterprises that exceed a certain organizational size (banks, insurances, automotive) or provide products that require software to scale significantly (retail, entertainment). 367 | These companies are usually driven by different trade-offs than e.g. startup companies: 368 | 369 | * __Security of investment__ -- Enterprise software systems usually have a life cycle of decades. That means that enterprises have the need to choose technologies that will be around a couple of years ago. 370 | The availability of developers able to work with that technology is another aspect of this. 371 | It's also important to ship bug- and security fixed at a reasonable pace so that existing systems stay safe. 372 | * __Backwards compatibility and stability__ -- The just mentioned aspect is usually reflected in the choice of technology that has proven to ship stable, non-breaking releases containing new features. 373 | * __Availability of support__ -- Last but not least the backing of a commercial entity is usually required as a means of risk mitigation in case problems arise, teams run into bugs etc. 374 | 375 | //// 376 | == Diagrams 377 | 378 | [plantuml, di-1] 379 | ---- 380 | class MyService { 381 | +someBusinessMethod() : void 382 | } 383 | class MyRepository { 384 | +save(entity : MyEntity) : MyEntity 385 | } 386 | 387 | MyService -right-> MyRepository 388 | ---- 389 | 390 | [plantuml, di-2] 391 | ---- 392 | class MyService { 393 | -repository : MyRepository 394 | +someBusinessMethod() : void 395 | } 396 | class MyRepository { 397 | +save(entity : MyEntity) : MyEntity 398 | } 399 | 400 | MyService -right-> MyRepository 401 | ---- 402 | 403 | [plantuml, di-3] 404 | ---- 405 | class MyService { 406 | -repository : MyRepository 407 | +MyService() 408 | +someBusinessMethod() :void 409 | } 410 | class MyRepository { 411 | +save(entity : MyEntity) : MyEntity 412 | } 413 | 414 | MyService -right-> MyRepository 415 | ---- 416 | 417 | [plantuml, di-4] 418 | ---- 419 | class MyService { 420 | -repository : MyRepository 421 | +MyService(MyRepository : repository) 422 | +someBusinessMethod() : void 423 | } 424 | class MyRepository { 425 | +save(entity : MyEntity) : MyEntity 426 | } 427 | 428 | MyService -right-> MyRepository 429 | ---- 430 | 431 | [plantuml, di-5] 432 | ---- 433 | class MyService { 434 | -repository : MyRepository 435 | +someBusinessMethod() : void 436 | } 437 | interface MyRepository { 438 | +save(entity : MyEntity) : MyEntity 439 | } 440 | class JpaRepository { 441 | +save(entity : MyEntity) : MyEntity 442 | } 443 | 444 | MyService -right-> MyRepository 445 | JpaRepository .left.|> MyRepository 446 | ---- 447 | 448 | [plantuml, di-6] 449 | ---- 450 | class MyService { 451 | -repository : MyRepository 452 | +someBusinessMethod() : void 453 | } 454 | interface MyRepository { 455 | +save(entity : MyEntity) : MyEntity 456 | } 457 | class JpaRepository { 458 | +save(entity : MyEntity) : MyEntity 459 | } 460 | class DummyRepository { 461 | +save(entity : MyEntity) : MyEntity 462 | } 463 | 464 | 465 | MyService -right-> MyRepository 466 | JpaRepository .left.|> MyRepository 467 | DummyRepository .up.|> MyRepository 468 | ---- 469 | 470 | [plantuml, di-0] 471 | ---- 472 | skinparam packageStyle rectangle 473 | 474 | package "business" { 475 | class MyService 476 | } 477 | 478 | package "data access" { 479 | class MyRepository 480 | } 481 | ---- 482 | //// 483 | --------------------------------------------------------------------------------