{"id":15988729,"url":"https://github.com/michael-simons/ws-20170627-cluj","last_synced_at":"2026-01-19T21:33:02.246Z","repository":{"id":73723188,"uuid":"94544390","full_name":"michael-simons/ws-20170627-cluj","owner":"michael-simons","description":"Workshop at Accesa","archived":false,"fork":false,"pushed_at":"2017-06-30T14:51:23.000Z","size":424,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-10-13T02:09:22.851Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/michael-simons.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-2.0.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-06-16T13:02:32.000Z","updated_at":"2017-08-11T16:10:47.000Z","dependencies_parsed_at":"2023-05-18T14:15:53.764Z","dependency_job_id":null,"html_url":"https://github.com/michael-simons/ws-20170627-cluj","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/michael-simons/ws-20170627-cluj","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michael-simons%2Fws-20170627-cluj","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michael-simons%2Fws-20170627-cluj/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michael-simons%2Fws-20170627-cluj/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michael-simons%2Fws-20170627-cluj/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/michael-simons","download_url":"https://codeload.github.com/michael-simons/ws-20170627-cluj/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michael-simons%2Fws-20170627-cluj/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28585511,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-19T20:45:59.482Z","status":"ssl_error","status_checked_at":"2026-01-19T20:45:41.500Z","response_time":67,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-08T04:20:59.152Z","updated_at":"2026-01-19T21:33:02.226Z","avatar_url":"https://github.com/michael-simons.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"= An introduction to Spring Boot\nMichael Simons \u003cmichael@simons.ac\u003e\n:revnumber: 1.0\n:revdate: {docdatetime}\n:toc:\n:toc-placement!:\n:source-highlighter: prettify\n:sectanchors:\n:icons: font\n:experimental:\n\n(C) 2017 The original authors.\n\nNOTE: _Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically._\n\ntoc::[]\n:sectnums:\n:sectnumlevels: 2\n\n== Abstract\n\nIn this workshop we'll work on a application that deals with events (like talks, conferences and so on). The scope of the application is to store those events and provide an API for retrieving, manipulating and updating events.\n\nIf we'll have some time left, we also gonna see a short demo how to orchestrate that as a microservice for a calendar application.\n\n== Setup\n\n=== Prerequisites\n\nYou need an installed JDK, at least Java 8. If you use https://git-scm.com/[Git], then you can checkout the Code using Git.\n\nThe project in this workshop will be a https://maven.apache.org[Maven] project. It contains the Maven wrapper, so you don't have to install Maven by hand if you don't have a recent copy.\n\nAn installed IDE is recommended but not necessary. Having `cURL`, a browser or, any other sophisticated HTTP client installed is beneficial for issuing HTTP requests.\n\nAs Spring Boot Project is a plain Gradle or Maven Project in the end, all three major IDEs (https://netbeans.org[NetBeans], https://spring.io/tools/sts[Eclipse / Spring Tool Suite] and https://www.jetbrains.com/idea/[IntelliJ IDEA]) can open and compile them. Additional support like code completion for configuration files, request mappings, dashboards and so on vary from IDE to IDE. You have to decide what you want and need.\n\nIf you want to try out the microservice examples in Docker, you'll need a recent Docker installation on your machine, too.\n\nWARNING: If you don't have docker installed, the projects won't build after commit #445fcec. I have tagged the latest version that works without docker for you, so you can check it out with `git checkout no-docker`.\n\n==== Downloads\n\n* http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html[Oracle JDK], please choose your operating system\n* https://git-scm.com[Git]\n* One of those:\n** https://spring.io/tools/sts[Spring Tool Suite]\n** https://netbeans.org[NetBeans] with https://github.com/AlexFalappa/nb-springboot[NB-SpringBoot]\n** https://www.jetbrains.com/idea/[IntelliJ IDEA]\n* https://www.docker.com/community-edition#/download[Docker Community Edition], please choose your operating system\n\n\n=== Register a GitHub account (Optional, recommended)\nGo to https://github.com and signup for a free account. If you already have a GitHub account, you can skip this step.\n\n[[clone-the-repo]]\n=== Clone the repository\n\nThe code of this project will be available at GitHub at https://github.com/michael-simons/ws-20170627-cluj after the workshop.\n\n=== Prepare your IDE\n\nChoose one of\n\n* Spring Tool Suite (STS) from https://spring.io/tools\n* NetBeans from https://netbeans.org\n* Eclipse from https://www.eclipse.org\n* IntelliJ IDEA from https://www.jetbrains.com/idea/\n\nEven though Spring Boot projects are just regular Maven Projects, you'll get some perks from IDEs, for example direct integration with Spring Boots initializer, recognition of Spring Boots configuration, Spring Beans and more.\n\nSTS and IntelliJ offer the most complete support, however in the case of IntelliJ only the ultimate edition does. \n\nSTS is completely free. You can get its integration into Eclipse as well from the marketplace, but I'll recommend using STS directly.\n\nNetBeans is one of the best choices for Maven projects. It also features direct support of project Lombok for example. If you're a NetBeans user, I'll highly recommend to install _NB-SpringBoot_ from the plugin page or directly from NetBeans through menu:Tools[Plugins \u003e  Available Plugins]. Find more information on the plugin here: https://github.com/AlexFalappa/nb-springboot.\n\nWe'll discover some features of NetBeans, STS and IntelliJ on the way through the workshop, but let me assure you once again: Spring Boot projects are just plain normal Maven or Gradle projects.\n\n\n== Create a new Spring Boot project\n\nNOTE: In this part you'll learn about the different ways to bootstrap a Spring Boot application.\n\nAs you have learned in the introduction, Spring Boot projects are basically Spring projects that just happend to configure stuff in a way that seems \"magically\" at first look. This configuration comes in the form of a parent project in the case of Maven and in the form of a plugin if you're a Gradle user.\n\nYou can write the build file by hand or you just can go to https://start.spring.io[start.spring.io] and have the Spring Initializr generate it for you.\n\n=== Use the Spring Initializr\n\n[[initializr-website]]\n==== Through the website\n\n*Steps*\n\n1. Go to https://start.spring.io, chose wether you want to use Maven or Gradle, the language (in this workshop we'll use Java) and the version of Spring Boot (leave as is).\n2. Enter \"Web\" inside dependencies textbox and select \"Web\"\n3. Repeat last steps with the technologies you'll find interesting\n4. Hit generate\n\nYour browser downloads a Zipfile with your project. You can unzip this with a tool of your choice and already start your application:\n\n.Unzip and start the first demo\n====\n[source,shell]\n----\nunzip demo.zip\ncd demo\n./mvnw spring-boot:run\n----\n====\n\nTo explore the generated buildfile and the minimal application skeleton generated, we're repeat the steps inside an IDE.\n\n==== From an IDE\n\n===== Spring Tools Suite\n\n*Steps*\n\n* Choose menu:File[New \u003e Spring Starter Project]\n* Enter `event-service` as a name\n* Enter `ac.simons.ws.cluj:events` as coordinates (feel free to chose whatever you like, though. However: this document and the finished project will refer to those coordinates)\n* Enter some appropriate description and name\n* Clear the package name\n* Hit next and chose \"Web\" as dependency\n* Hit either finish or next. Next gives you a handy URL that can be used to reference exactly the options you used\n\nWait a second until STS automatically opens your project.\n\n===== NetBeans\n\nThe steps are basically the same, but you have to have the NB-SpringBoot-Plugin installed.\n\n* Choose menu:File[New Project… \u003e Maven \u003e Spring Initializr Project]\n* Enter your coordinates and description as described above\n* Choose dependencies as described before\n* Select a location\n* Uncheck _Remove Maven Wrapper_ if you want to keep the Maven Wrapper inside the generated project (which is useful, if you ask me)\n\nYou're done.\n\nYou'll notice that neither STS nor NetBeans store a lot of IDE specific stuff. Both have some project specific settings, like which is the main class and so on, but that's basically it. You can run both projects from the command line.\n\n===== IntelliJ IDEA Ultimate Edition\n\nAgain, the same steps. Hit \"Create new project\", choose \"Spring Initializr\" and follow the dialog.\n\n==== With cURL or other tools\n\nThe Spring Initializr is completely scriptable. It speaks Hypertext Application Language (HAL) and you can get an overview about its options by just curling it:\n\n.Retrive the metadata of Spring Initializr\n====\n[source,shell]\n----\ncurl -H 'Accept: application/json' https://start.spring.io\n----\n====\n\nFind a complete documentation at http://docs.spring.io/initializr/docs/current/reference/htmlsingle/#metadata-format[docs.spring.io/initializr]. The example from \u003c\u003cinitializr-website,the beginning of this chapter\u003e\u003e also be created via\n\n.Use Spring Initializr from the command line\n====\n[source,shell]\n----\ncurl https://start.spring.io/starter.zip -d dependencies=web -o demo.zip\nunzip demo.zip -d demo\ncd demo\n./mvnw spring-boot:run\n----\n====\n\nNOTE: Explore the manual linked above. You can install a custom copy of the Initializr if you want to. Scripting it might be a valuable tool for your process.\n\n== Explore your first Spring Boot project\n\nIf you didn't follow the steps above, now it's time to open the project `event-service`. Navigate to the folder that you used while \u003c\u003cclone-the-repo,cloning the repository\u003e\u003e and open the project with your IDE of choice. \n\n=== The build file\n\nFirst check out the `pom.xml`. Note that it defines a parent through\n\n.Standard Spring Boot projects inherited from Spring Boots parent pom\n====\n[source,xml]\n----\n\u003cparent\u003e\n\t\u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n\t\u003cartifactId\u003espring-boot-starter-parent\u003c/artifactId\u003e\n\t\u003cversion\u003e1.5.4.RELEASE\u003c/version\u003e\n\t\u003crelativePath/\u003e\n\u003c/parent\u003e\n----\n====\n\nThen follow some properties. Have a look at `java.version`. Its set to 1.8 by default. You don't have to configure Mavens compiler plugin. The parent does that for you.\n\nNOTE: Spring Boot prior to 2 supports Java 7 and 8 (Java 6 with some workarounds), Spring Boot 2 needs Java 8 and will support Java 9.\n\n==== Build-in dependency management\n\nThen follow the dependencies, which we just declared:\n\n.Some Spring Boot dependencies\n====\n[source,xml]\n----\n\u003cdependencies\u003e\n\t\u003cdependency\u003e\n\t\t\u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n\t\t\u003cartifactId\u003espring-boot-starter\u003c/artifactId\u003e\n\t\u003c/dependency\u003e\n\t\u003cdependency\u003e\n\t\t\u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n\t\t\u003cartifactId\u003espring-boot-starter-web\u003c/artifactId\u003e\n\t\u003c/dependency\u003e\n\n\t\u003cdependency\u003e\n\t\t\u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n\t\t\u003cartifactId\u003espring-boot-starter-test\u003c/artifactId\u003e\n\t\t\u003cscope\u003etest\u003c/scope\u003e\n\t\u003c/dependency\u003e\n\u003c/dependencies\u003e\n----\n====\n\nNoticeable here is the lack of version numbers. That has been taken care of by the Spring and Spring Boot teams: They chose versions of libraries that work well together and put them in their parent pom in a section `\u003cdependencyManagement /\u003e` Now everytime you need one of those, you can skip the version number.\n\nIf you want to override a version, you can do that too via a property declared like the Java version. Later in the example we're gonna use Flyway and declare it like so\n\n.Another dependency without explicit version\n====\n[source,xml]\n----\n\u003cdependency\u003e\n\t\u003cgroupId\u003eorg.flywaydb\u003c/groupId\u003e\n\t\u003cartifactId\u003eflyway-core\u003c/artifactId\u003e\n\u003c/dependency\u003e\n----\n====\n\nIf you want to use a version other than Spring Boot uses, you wouldn't overwrite it in the dependency but declare a property:\n\n.Overwriting versions via properties\n====\n[source,xml]\n----\n\u003cproperties\u003e\n\t\u003cflyway.version\u003e4.2.0\u003c/flyway.version\u003e\n\u003c/properties\u003e\n----\n====\n\n==== \"Starter\"\n\nWhat the hell are starter? Those are \"meta-dependencies\". They usually consist of an autoconfigure module that has code for configuring certain aspects of a framework module or library. Usually that autoconfigure module depends only optional on the framework module or library. The starter module itself depends on the autoconfiguration as well as on the libraries. \n\n=== The main class\n\nDepending on how you parameterized the initializer, your main class will be named differently. In the example project it is `EventServiceApplication`, named after the coordinates you entered and located in the package, which you entered explicitly. The class looks pretty innocent:\n\n.A default Spring Boot main class\n====\n[source,java]\n----\npackage ac.simons.ws.cluj.events;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class EventServiceApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(EventServiceApplication.class, args);\n\t}\n}\n----\n====\n\nThe main method is interesting, because it delegates to a static helper method inside `SpringApplication` that does all the heavy lifting of initializing the Spring context.\n\nBut where does the configuration come from? Explore the `@SpringBootApplication` annotation. It's a composed annotation that looks like this:\n\n.Abbreviated source of `@SpringBootApplication`\n====\n[source,java]\n----\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@SpringBootConfiguration\n@EnableAutoConfiguration\n@ComponentScan\npublic @interface SpringBootApplication{}\n----\n====\n\nThis annotation combines several others:\n\n* `@SpringBootConfiguration` marks the class as a configuration class. `@SpringBootConfiguration` is special in so far that there should be only one of those throughout one context whereas `@Configuration` classes can be many\n* `@EnableAutoConfiguration` enables the \"magic\" of Spring Boot: It looks for `@Configuration` classes that are marked as autoconfiguration and loads them fully or partially, depending on wether certain conditions are fulfilled or not.\n* `@ComponentScan` finally kicks of the search for Spring components. Those are `@Controller`, `@Services` and many others\n\nAs you can see, the application class has a main method and therefor can be used for a runnable jar file. Part of Spring Boots philosophy is to provide single fat jars as deployment artifacts. \n\nAs we have declared the WEB dependency and got the `spring-boot-starter-web`, we also have an embedded Tomcat on the classpath.\n\nHowever, you can also choose to deploy Spring Boot applications as war. If you had chose 'WAR' as packaging type in the initializer, it would have created an additional `SpringBootServletInitializer` that facilitates Springs SPI for `ServletContainerInitializer`.\n\nHow does that work with the embedded tomcat? There are Maven and Gradle plugins that repackages the artifact to be a \"fat jar\" or \"fat war\". The \"main\" class we have written isn't directly called, but a Spring Boot loader class. \n\n=== The package structure\n\nAt the moment, there's only one class, the application class. The package is based on the project coordinates. The package structure is actually already important here as the application class is annotated with `@ComponentScan`. This annotations searches for Spring components from the package the annotated class declares downwards.\n\nThere are two caveats: \n\n* Don't annotate a class in the root package with `@SpringBootApplication` or `@ComponentScan`. It will scan the whole class path!\n* Those annotation won't scan packages in parallel to their current package\n\nHow about the configuration in starter than? Does Spring actually run a full classpath package scan? No: It uses the `spring.factories` services locator implementation!\n\n=== The test sources\n\nThe initializer already generated a test:\n\n.Generated test\n====\n[source,java]\n----\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class EventServiceApplicationTests {\n\n\t@Test\n\tpublic void contextLoads() {\n\t}\n}\n----\n====\n\nIts a standard JUnit 4 test that is run with a special runner, the `SpringRunner` and marked as `@SpringBootTest`. The later denotes a full integration test: Loading the embedded web container (if any) and all connections to third party services. \n\n== Add some content\n\n=== Spring Boot Developer Tools\n\nLet's add a nice runtime dependency call \"Spring Boot Developer Tools\", \"devtools\" for short:\n\n.Declare Spring Boot devtools\n====\n[source,xml]\n----\n\u003cdependency\u003e\n\t\u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n\t\u003cartifactId\u003espring-boot-devtools\u003c/artifactId\u003e\n\t\u003cscope\u003eruntime\u003c/scope\u003e\n\u003c/dependency\u003e\n----\n====\n\nThis is a not a compile time but a runtime dependency that has several neat features: \n\n* When the application is run from an IDE or with the Maven or Gradle plugin, it restarts the context when classes changes\n* It automatically reloads changed resources\n* Changes some settings during development, for example disables caching for messages, templates and so on\n\nIt’s not as elaborate like JRebel, but, nevertheless, valuable!\n\n=== Spring Boot Starter JPA\n\nWe have to store some stuff. `org.springframework.boot:spring-boot-starter-data-jpa` is a handy starter that brings you among others:\n\n* Hibernate Core\n* Hibernate Entity Manager\n* Spring JDBC\n* Spring Data Commons\n* Spring Data JPA\n\nimage::handout/data-jpa-deps.png[]\n\nWe gonna deep dive into this later. First step is to declare a simple entity for storing events:\n\n.Simple JPA entity\n====\n[source,java]\n----\n@Entity\n@Table(\n        name = \"events\",\n        uniqueConstraints = {\n            @UniqueConstraint(name = \"events_uk\", columnNames = {\"held_on\", \"name\"})\n        }\n)\npublic class EventEntity implements Serializable {\n\n    private static final long serialVersionUID = 2005305860095134425L;\n\n    public enum Status {\n\n        open, closed\n    }\n\n    @Id\n    @GeneratedValue(strategy = GenerationType.IDENTITY)\n    private Integer id;\n\n    @Column(name = \"held_on\", nullable = false)\n    @Temporal(TemporalType.TIMESTAMP)\n    private Calendar heldOn;\n\n    @Column(length = 512, nullable = false)\n    private String name;\n\n    @Column(name = \"created_at\", nullable = false)\n    @Temporal(TemporalType.TIMESTAMP)\n    private Calendar createdAt;\n\n    @Enumerated(EnumType.STRING)\n    private Status status;\n\n    protected EventEntity() {\n    }\n\n    public EventEntity(final Calendar heldOn, final String name) {\n        this.heldOn = heldOn;\n        this.name = name;\n        this.status = Status.open;\n    }\n\n    @PrePersist\n    @PreUpdate\n    void prePersistAndUpdate() {\n        if (this.createdAt == null) {\n            this.createdAt = Calendar.getInstance();\n        }\n    }\n}\n----\n====\n\nAs you can see: Nothing apart from Hibernate specific annotations and nothing fancy here.\n\nUsing Spring with Spring Boot and the JPA starter you don't have to worry about a persistence unit. Spring Boot takes care of\n\n* Collecting all entity classes and related classes\n* Provides a datasource\n* Provides local transaction management for that datasource \n* Provides an EntityManagerFactory \n* Provides a thread safe entity manager, independent wether JTA or application based transactions are used -\u003e You can omit `@PersistenceContext` annotation which works on attributes only\n\n=== Web layer\n\nWe already have all the dependencies we need to work on the web layer, so we can add a controller that should take care of handling events:\n\n.Empty Spring Web MVC controller\n====\n[source,java]\n----\n@RestController\npublic class EventApi {\n}\n----\n====\n\nNothing there yet, we're gonna develop the controller in a test driven way.\n\nInside your test sources create a class `EventApiTest` in the same package as your controller having the following content:\n\n.Test the controller above\n====\n[source,java]\n----\n@RunWith(SpringRunner.class)\n@WebMvcTest\npublic class EventApiTest {\n    @Test\n    public void getEventsShouldWork() {\n        // Actually test something ;)\n    }\n}\n----\n====\n\nWhat do we have here: Again a Spring JUnit Test, but this time `@WebMvcTest`. This annotation is called a _test slice_. It only configures infrastructure and Spring components essential for that technical slice. In this case: The web layer. \n\nNOTE: `@WebMvcTest` first scans the package of the test class for a context configuration. If it doesn't find a class with `@SpringBootApplication` it uses the same rules as the Spring Framework itself, i.e. it looks also for XML and Groovy based configuration. If that isn't successful, it also scans the class path \"upwards\", so it finds your main Spring Boot class even if you have put away your controllers inside a subpackage.\n\n.First iteration of the test\n====\n[source,java]\n----\n@Autowired\nprivate MockMvc mvc;\n\n@Test\npublic void getEventsShouldWork() throws Exception {\n    this.mvc\n            .perform(MockMvcRequestBuilders.get(\"/api/events\"))\n            .andExpect(MockMvcResultMatchers.status().isOk());\n}\n----\n====\n\nThat first approach uses an instance of `MockMvc` provided by `@WebMvcTest` to call the events api and expects a status 200 (ok). That test obviously fails. Let's make this work by adding one method to the controller:\n\n.Fix the failing test\n====\n[source,java]\n----\n@GetMapping(\"/api/events\")\npublic List\u003cEventEntity\u003e getEvents() {\n    return new ArrayList\u003c\u003e();\n}\n----\n====\n\nNow run the test again and see it turn green!\n\nWe can easily break it again by actually checking the result:\n\n.Break the test again!\n====\n[source,java]\n----\n@Test\npublic void getEventsShouldWork() throws Exception {\n    this.mvc\n            .perform(MockMvcRequestBuilders.get(\"/api/events\"))\n            .andExpect(MockMvcResultMatchers.status().isOk())\n            .andExpect(MockMvcResultMatchers.jsonPath(\"$[0].name\", equalTo(\"test1\")));\n}\n----\n====\n\nThis one checks if the returned content is actually valid. Naive implementation would be hitting the entity manager from within the controller but how could we test just the controller and keeping away from the database? Let's introduce a service:\n\n.Simple service that does the heavy lifting for us\n====\n[source,java]\n----\n@Service\npublic class EventService {\n    public List\u003cEventEntity\u003e allEvents() {\n        return new ArrayList\u003c\u003e();\n    }\n}\n----\n====\n\nThis class is picked up by the Spring context and can be injected into other beans. Lets cleanup the controller in the way:\n\n.Revamped controller\n====\n[source,java]\n----\n@RestController\n@RequestMapping(\"/api/events\")\npublic class EventApi {\n    private final EventService eventService;\n\n    public EventApi(EventService eventService) {\n        this.eventService = eventService;\n    }\n    \n    @GetMapping\n    public List\u003cEventEntity\u003e getEvents() {\n        return this.eventService.allEvents();\n    }\n}\n----\n====\n\nYou see only annotations relevant to Spring Web MVC. Try running the test know: It doesn't even start any more: The Service is not part of the webslice! Spring Boot actually helps you a lot here with its failure analyzers:\n\n.Failure analysis\n====\n....\nError starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.\n2017-06-19 12:07:57.297 ERROR 73221 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : \n\n***************************\nAPPLICATION FAILED TO START\n***************************\n\nDescription:\n\nParameter 0 of constructor in ac.simons.ws.cluj.events.EventApi required a bean of type 'ac.simons.ws.cluj.events.EventService' that could not be found.\n\n\nAction:\n\nConsider defining a bean of type 'ac.simons.ws.cluj.events.EventService' in your configuration.\n....\n====\n\nInstead of providing the real bean, we're gonna use `@MockBean`:\n\n.Providing collaborateurs through `@MockBean`\n====\n[source,java]\n----\n@RunWith(SpringRunner.class)\n@WebMvcTest\npublic class EventApiTest {\n\n    @Autowired\n    private MockMvc mvc;\n\n    @MockBean\n    private EventService eventService;\n}\n----\n====\n\nThat would be possible on class level, too, but we're gonna need that been together with the Mockito support that the dependency on `org.springframework.boot:spring-boot-starter-test` brought:\n\n.Prepare the mock\n====\n[source,java]\n----\nZonedDateTime now = ZonedDateTime.now();\n        List\u003cEventEntity\u003e expectedEvents\n                = Arrays.asList(\n                        new EventEntity(GregorianCalendar.from(now.plusDays(3)), \"test1\"),\n                        new EventEntity(GregorianCalendar.from(now.plusWeeks(1)), \"test2\")\n                );\n        Mockito.when(eventService.allEvents()).thenReturn(expectedEvents);\n----\n====\n\n=== Database layer\n\nNow that we have tested the web layer, we'll move back again to the database. Together with the Spring Data JPA dependencies we have added `com.h2database:h2` as a runtime dependencies. Spring Boot configures an in-memory instance of H2 if no other database connection is configured. So, we already have a datasource.\n\nWhere does the schema come from? Spring Boot configures `spring.jpa.hibernate.ddl-auto` for you: It uses `create-drop` on an embedded database, `none` otherwise which is a sensible default.\n\nSpring Boot also takes `schema.sql` and `data.sql` scripts in the root of the classpath in consideration. If there is a `schema.sql` Spring uses that for database initialization *before* `data.sql` *and* before JPA. If there's only `data.sql`, Spring Boot uses first JPA to generate schema and than the script.\n\nIn the example we're gonna use `data.sql` only and let Hibernate generate the schema for us.\n\nFirst of all we're gonna rework the generated test. By default it's only mocking the web environment. Let's start in on a random port through `@SpringBootTest(webEnvironment = RANDOM_PORT)`. That gives also a `TestRestTemplate` that makes calling our a API a breeze:\n\n.Test first, again\n====\n[source,java]\n----\n@RunWith(SpringRunner.class)\n@SpringBootTest(webEnvironment = RANDOM_PORT)\npublic class EventServiceApplicationTests {\n\n    @Autowired\n    private TestRestTemplate restTemplate;\n\n    @Test\n    public void getEventsShouldWork() {\n        final List\u003cEventEntity\u003e events = this.restTemplate.exchange(\n                \"/api/events\", \n                HttpMethod.GET, \n                null, \n                new ParameterizedTypeReference\u003cList\u003cEventEntity\u003e\u003e() {}\n        ).getBody();\n        assertThat(\n                events.get(0).getName(), \n                is(equalTo(\"Get the most out of your data layer\"))\n        );\n    }\n}\n----\n====\n\nThe test fails as expected, but safely assuming we do have some content now, let's fill the service with life and make the test work:\n\n.Event service based on plain JPA database access\n====\n[source,java]\n----\n@Service\npublic class EventService {\n    private final EntityManager entityManager;\n\n    public EventService(EntityManager entityManager) {\n        this.entityManager = entityManager;\n    }\n    \n    @Transactional(readOnly = true)\n    public List\u003cEventEntity\u003e allEvents() {\n        return this.entityManager\n                .createQuery(\n                        \"Select event from EventEntity event order by event.heldOn\", \n                        EventEntity.class\n                )\n                .getResultList();\n    }\n}\n----\n====\n\nAnd green again!\n\n\n==== Using a repository abstraction\n\nYour service should have to deal with the persistence storage on it's own, meaning, it should have no knowledge of the underlaying technology. \n\nThe repository pattern fixes that. Implemented in Spring Data JPA it takes away the burden of interacting with the persistence layer in many cases. \n\nThe abstraction is in so far leaking that you get JPA entites out of a JPA repository and Mongo documents out of a Mongo repository. It's up to you to encapsulate this further.\n\nWe have several tests in place and can try to rework our service. \n\nSpring Boot supports Spring Data JPA out of the box and you can declare a repository like this:\n\n.Spring Data JPA repository for the Events\n====\n[source,java]\n----\npublic interface EventRepository extends Repository\u003cEventEntity, Integer\u003e {\n    List\u003cEventEntity\u003e findAllByOrderByHeldOnAsc();\n}\n----\n====\n\nIf you already know Spring Data you'll notice that I don't used the `JpaRepository` interface as I prefer only to declare the methods I actually need and use.\n\nThe rewritten service based on that repository now looks like this:\n\n.Rewritten event service\n====\n[source,java]\n----\n@Service\npublic class EventService {\n    private final EventRepository eventRepository;\n\n    public EventService(EventRepository eventRepository) {\n        this.eventRepository = eventRepository;\n    }\n\n    public List\u003cEventEntity\u003e allEvents() {\n        return this.eventRepository.findAllByOrderByHeldOnAsc();\n    }\n}\n----\n====\n\nAnd our test is still green.\n\n==== Testing the service\n\nWe have several ways of testing the service. We can either mock the repository (which I usually do), or we can use yet another test slice, that is: `@DataJpaTest`. `@DataJpaTest` replaces databases via an embedded Database by default, runs all init scripts or migrations and then runs a transactional test. Transactional tests are rolled back by default:\n\n==== Other options to initialize a database\n\nAlthough you can use several `schema.sql` and `data.sql` scripts and also relay on JPA to generate your tables, I'm very sceptic about that. Especially the JPA based migrations work only well if you're and your application is in charge of the database. That is: Has the right to change schema at will.\n\nThe project contains therefor contains a dependency to Flyway (`org.flywaydb:flyway-core`) core, that takes care of using dedicated scripts to initialize your database. An alternative would be using Liquibase.\n\n== Configuration of Spring Boot Applications\n\nYou can follow several paradigms to configure a Spring Boot application. You're either can relay completely on the environment and let your Spring Boot application adapt itself or you chose profiles or combinations thereof. Either way: In no case you have to build your applications for different environments differently.\n\nYou should differentiate between external and internal configuration. We speak about internal configuration in regards of beans, context- and dependency injection and so on. External configuration on the other hand basically describes means to change the behavior of your application depending on environment or configuration properties.\n\nInternal configuration often depends on external configuration!\n\n=== External configuration\n\nExternal configuration come from a so called `PropertySource`. Those property sources can be of various kind and have a well defined order:\n\n* `@TestPropertySource` annotations on your tests.\n* `@SpringBootTest#properties` annotation attribute on your tests.\n* Command line arguments\n* Properties from `SPRING_APPLICATION_JSON` (inline JSON embedded in an environment\n* variable or system property)\n* `ServletConfig` init parameters\n* `ServletContext` init parameters\n* JNDI attributes from `java:comp/env`\n* Java System properties (`System.getProperties()`)\n* OS environment variables\n* A `RandomValuePropertySource` that only has properties in `+random.*+`\n* Profile-specific application properties outside of your packaged jar\n* (`application-{profile}.properties` and YAML variants)\n* Profile-specific application properties packaged inside your jar (`application-{profile}.properties` and YAML variants)\n* Application properties outside of your packaged jar (`application.properties` and YAML variants)\n* Application properties packaged inside your jar (`application.properties` and YAML variants)\n* `@PropertySource` annotations on your `@Configuration` classes.\n* Default properties (specified using `SpringApplication.setDefaultProperties`)\n\nNow, open the file `application.properties`. It corresponds to the `default` profile and configuration of your application.\n\nNOTE: If you're a YAML fan, you can use YAML as well. \n\nAll IDEs mentioned here support syntax highlighting and auto completion in this file.\n\n==== How to use those properties?\n\nYou basically have to options: \n\n1. The `@Value` annotation that can be used to retrieve any property from the environment.\n2. Classes annotated with `@ConfigurationProperties`\n\nThe second option has several advantages: \n\n* You can concentrate configuration for one topic in one class, including the possibility to provide defaults\n* The class are subject to relaxed binding\n* They are available as beans in the context\n* They are recognized by a build processor to generate meta data which in turn is helpful for content assist and other\n\n\nGiven the following pretty arbitrary properties bean:\n\n.Some arbitrary properties\n====\n[source,java]\n----\n@Component\n@ConfigurationProperties(prefix = \"event-service\")\npublic class EventServiceProperties {\n    /**\n     * The default number of seats avaiable for each event.\n     */\n    private Integer defaultNumberOfSeats;\n    \n    /**\n     * Some arbitrary information.\n     */\n    private String arbitraryInformation;\n\n    public Integer getDefaultNumberOfSeats() {\n        return defaultNumberOfSeats;\n    }\n\n    public void setDefaultNumberOfSeats(Integer defaultNumberOfSeats) {\n        this.defaultNumberOfSeats = defaultNumberOfSeats;\n    }\n\n    public String getArbitraryInformation() {\n        return arbitraryInformation;\n    }\n\n    public void setArbitraryInformation(String arbitraryInformation) {\n        this.arbitraryInformation = arbitraryInformation;\n    }\n}\n----\n====\n\nThe default number of seats can be configured by any of those:\n\n* `event-service.default-number-of-seats = 42`\n* `eventService.defaultNumberOfSeats = 42`\n* `event_service.default_number_of_seats = 42`\n\nChose the format that fits the source best: Usually uppercase and underscores works great in environment properties, dashes are good for properties.\n\nTo provide metadata of this configuration file for you, you're IDE and your coworkers add the `spring-boot-configuration-processor`\n\n.Use the spring-boot-configuration-processor\n====\n[source,xml]\n----\n\u003cdependency\u003e\n\t\u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n\t\u003cartifactId\u003espring-boot-configuration-processor\u003c/artifactId\u003e\n\t\u003coptional\u003etrue\u003c/optional\u003e\n\u003c/dependency\u003e\n----\n====\n\n== Non functional requirements\n\nSome non functional requirements that usually are requested:\n\n* Health information\n* Metrics\n* Context and configuration information\n* Logging\n\nSpring Boot offers Spring Boot Actuator that can be added as simple dependency:\n\n.Add Spring Boot Actuator\n====\n[source,xml]\n----\n\u003cdependency\u003e\n\t\u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n\t\u003cartifactId\u003espring-boot-starter-actuator\u003c/artifactId\u003e\n\u003c/dependency\u003e\n----\n====\n\nThis provides a several interesting new api endpoints, including the information above.\n\nWith current Spring Boot they reside directly under the root context:\n\n* http://localhost:8080/health\n* http://localhost:8080/metrics\n* http://localhost:8080/info\n* http://localhost:8080/autoconfig\n\nSpring Boot will change that to `/application/` and you can configure that already today. And while we're at it, disable actuator endpoint security as the endpoints are protected by default and since we don't have Spring Security yet, there's no valid user to authenticate with:\n\n.Configure Spring Boot actuator\n====\n[source,properties]\n----\nmanagement.security.enabled = false\n# That will be the default with Spring Boot 2.0\nmanagement.context-path = /application\n----\n====\n\n== Microservice Scenario\n\nThe sources contain one microservice scenario which makes use of two projects from the https://cloud.spring.io/spring-cloud-netflix/[Spring Cloud Netflix] portfolio:\n\n* It uses Eureka for Service Discovery: Eureka instances can be registered and clients can discover the instances using Spring-managed beans\n* It uses Hystrix as a service breaker: Hystrix clients can be built with a simple annotation-driven method decorator\n\nNOTE: This is just a demonstration of how one can use those projects. It should not by any means recommend this kind of microservice architecture for all use cases. Its a nice and easy way to demonstrate what you can do with additional Spring projects after finishing your Spring Boot application.\n\nThe project `calendar-service` is a client of `event-service`. In addition there is now `service-registry`.\n\n`service-registry` declares an additional dependency management:\n\n.Bring in Spring Cloud dependencies\n====\n[source,xml]\n----\n\u003cdependencyManagement\u003e\n\t\u003cdependencies\u003e\n\t\t\u003cdependency\u003e\n\t\t\t\u003cgroupId\u003eorg.springframework.cloud\u003c/groupId\u003e\n\t\t\t\u003cartifactId\u003espring-cloud-dependencies\u003c/artifactId\u003e\n\t\t\t\u003cversion\u003e${spring-cloud.version}\u003c/version\u003e\n\t\t\t\u003ctype\u003epom\u003c/type\u003e\n\t\t\t\u003cscope\u003eimport\u003c/scope\u003e\n\t\t\u003c/dependency\u003e\n\t\u003c/dependencies\u003e\n\u003c/dependencyManagement\u003e\n\n\u003cdependencies\u003e\n\t\u003cdependency\u003e\n\t\t\u003cgroupId\u003eorg.springframework.cloud\u003c/groupId\u003e\n\t\t\u003cartifactId\u003espring-cloud-starter-eureka-server\u003c/artifactId\u003e\n\t\u003c/dependency\u003e\n\u003c/dependencies\u003e\n----\n====\n\nAnd the application is annotated like this:\n\n.Provide an Eureka server:\n====\n[source,java]\n----\n@SpringBootApplication\n@EnableEurekaServer\npublic class ServiceRegistryApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(ServiceRegistryApplication.class, args);\n\t}\n}\n----\n====\n\nThus the Spring Boot application boots the Eureka instance. In addition it tries to register itself with other instances. As it's the only one, disable this:\n\n.Disable registration with other registry\n====\n[source,properties]\n----\nserver.port = 8761\n\neureka.client.register-with-eureka = false\neureka.client.fetch-registry = false\n----\n====\n\nThe listing above also sets Springs server port to Eurekas default. You build and run the service registry as usual. If you do this, you'll reach a admin interface at http://localhost:8761[http://localhost:8761].\n\nThen back to the event service. Add the same additional dependency management as well as `org.springframework.cloud:spring-cloud-starter-eureka-server`.\n\nAlso, give the service a fixed name through configuration:\n\n.Fixed name for a Spring Boot application\n====\n[source,properties]\n----\nspring.application.name = event-service\n----\n====\n\nYou'll now annotate the `EventServiceApplication` with `@EnableDiscoveryClient`\n\n.Register a service with the service registry\n====\n[source,properties]\n----\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class EventServiceApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(EventServiceApplication.class, args);\n\t}\n}\n----\n====\n\n`@EnableDiscoveryClient` enables discovery for Eureka instances by default, but there are also implementations for Zookeeper and other.\n\nImagine the `event-service` as provider. The additional `calendar-service` in this repository is a client. The `calendar-service`  should retrieve all existing events and present them in a calendar widget. It's main application class shall also be annotated with `@EnableDiscoveryClient`.\n\nBoth services are now registered with Eureka.\n\nWe'll use Springs `RestTemplate` to synchronously `GET` `/api/events` from our event service, but we won't be hardcoding the server path and port of the event service. Instead, we'll provide a special instance of `RestTemplate` through the following configuration inside `calendar-service`:\n\n.Provide a load balanced RestTemplate\n====\n[source,java]\n----\n@Configuration\npublic class RestTemplateConfig {\n\n    @LoadBalanced\n    @Bean\n    public RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n}\n----\n====\n\nYou see an ordinary configuration class that returns one bean. The method is annotated with `@LoadBalanced`. `@LoadBalanced` also comes with `spring-cloud-starter-eureka-server` and is part of Springs so called Ribbon client. Ribbon is a client side load balancer that knows the connection to the service discovery. If you use `@EnableDiscoveryClient` with your application and are ok with Ribbons default, you can use it immediately to make your `RestTemplate` aware of virtual host names. \n\nVirtual host names are the names of the applications that are registered with the Eureka service registry instance.\n\nThe only difference in using the template is the fact, that you have to provide the name of a virtual host instead of a server and port:\n\n.Using a load balanced RestTemplate\n====\n[source,java]\n----\n@Service\npublic class EventService {\n    public List\u003cEvent\u003e allEvents() {\n        final ResponseEntity\u003cList\u003cEvent\u003e\u003e response =  this.restTemplate\n                .exchange(\"http://event-service/api/events\", HttpMethod.GET, null, new ParameterizedTypeReference\u003cList\u003cEvent\u003e\u003e() {\n                });\n        if(response.getStatusCode() != HttpStatus.OK) {\n            throw new RuntimeException(\"Could not retrieve events\");\n        }\n        return response.getBody();\n    }\n}\n----\n====\n\nAnd that's all! As you see, we have wrapped the call inside a service to which we'll come back later.\n\nUsing the service from with in a controller like this posses a problem though:\n\n.Using the service above\n====\n[source,java]\n----\n@Controller\npublic class IndexController {\n    \n    private final EventService eventService;\n\n    public IndexController(EventService eventService) {\n        this.eventService = eventService;\n    }\n    \n    @GetMapping({\"/\", \"index\"})\n    public String index(final Model model) {\n        model.addAttribute(\"events\", this.eventService.allEvents());\n        return \"index\";\n    }\n}\n----\n====\n\nThe request against the controller depends on another request from server to server that can fail (and usually will fail some times).\n\nThere are many different solutions for that topic. One is a technical solution called \"circuit breaker\", much like a fuse inside your house that opens when an error happens and depending on the fuse will close again, either manually or after some time, trying again.\n\nFrom the Netflix stack there's `org.springframework.cloud:spring-cloud-starter-hystrix` that provides `@HystrixCommand`. To use those circuit breakers inside your application, they have to be enabled like:\n\n.Enable circuit breaker\n====\n[source,java]\n----\n@SpringBootApplication\n@EnableCircuitBreaker\npublic class CalendarServiceApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(CalendarServiceApplication.class, args);\n\t}\n}\n----\n====\n\nAnd then it's easy to use them. In this case we're using the `fallbackMethod` attribute to define another method that is to be called when the original method fails for some reason:\n\n.Using a circuit breaker\n====\n[source,java]\n----\n@HystrixCommand(fallbackMethod = \"emptyEvents\")\npublic List\u003cEvent\u003e allEvents() {\n    // As above make call to remote server\n    return response.getBody();\n}\n\nList\u003cEvent\u003e emptyEvents() {\n    return new ArrayList\u003c\u003e();\n}\n----\n====\n\nDepending on other settings, Hystrix will keep the circuit open for some time, than close and try again.\n\nNOTE: Take care in a production system to adapt Hystrix timeouts to Ribbon clients timeouts.\n\nThat may works when a service depends on one remote call, but having different calls, it gets hard very fast to manage. A better solution for that may be architectures that invert the relation ship: Whereas we'll keep the events as a resource, that resources is polled in some sane interval and events are stored redundant in the calendar service. \n\nChanges from the calendar service to events can be communicated maybe through an event bus.\n\n== Wrap up\n\nWe have managed to setup one of many possible micro service architectures in about 4 hours. \n\nYou can run them independent now each as explained in the beginning and than hit http://localhost:8088 for the calendar service or use the provided docker images as follows (given a valid Docker installation on your machine)\n\nBuild everything with either `build.sh` or `build.bat`. That will use https://github.com/fabric8io/docker-maven-plugin[Docker Maven Plugin] to provide three images:\n\n* msimons/ws-cluj-calendar-service\n* msimons/ws-cluj-event-service   \n* msimons/ws-cluj-service-registry\n\nWhich are orchestrated via the following `docker-compose.yml`\n\n.Orchestrate docker images via docker-compose\n====\n[source,yml]\n----\ninclude::docker-compose.yml[]\n----\n====\n\nNotice how some properties of your applications in the containers can easily changed through dockers environment setting. For example we have to configure Eurekas default zone now with `eureka.client.serviceUrl.defaultZone` as Eureka doesn't run on the same host anymore.\n\nRun everything now with `docker-compose up` and the calendar service is reachable as a frontend for the whole system under http://localhost:8080.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichael-simons%2Fws-20170627-cluj","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmichael-simons%2Fws-20170627-cluj","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichael-simons%2Fws-20170627-cluj/lists"}