├── .gitignore
├── README.adoc
├── pom.xml
└── src
└── main
├── java
└── demo
│ ├── DemoApplication.java
│ ├── SecurityConfig.java
│ ├── domain
│ ├── Speaker.java
│ └── SpeakerRepository.java
│ └── web
│ └── HomeController.java
└── resources
├── application.properties
├── import.sql
└── static
└── error
└── 404.html
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *.iml
3 | target
4 |
--------------------------------------------------------------------------------
/README.adoc:
--------------------------------------------------------------------------------
1 | == Spring Boot Introduction (conference format)
2 |
3 | This is the sample app used during an introduction to Spring Boot (conference format,
4 | ~ 50min).
5 |
6 | We start from scratch and generate a secured web application that exposes a domain object
7 | (`Speaker`) as a REST endpoint.
8 |
9 | NOTE: You can bootstrap a simple web-based project at https://start.spring.io
10 |
11 | === Running the app locally
12 |
13 | There are many ways to start your Spring Boot app locally:
14 |
15 | * Run the `main` method in `DemoApplication` right from your IDE
16 | * On the command-line, execute `mvn spring-boot:run`
17 | * Package the project (`mvn package`) and _just_ execute the jar, that is
18 | `java -jar target/spring-boot-intro-conference-0.0.1-SNAPSHOT.jar`
19 |
20 | The H2 database has some default speakers created via `import.sql`. You can use curl to
21 | add more; to create a new `Speaker`, just _POST_ the following document to your local
22 | repository:
23 |
24 | ```json
25 | {
26 | "firstName": "John",
27 | "lastName": "Smith",
28 | "twitter": "yourTwitterAccountHere",
29 | "github": "youtGithubAccountHere",
30 | "bio": "John Smith's bio."
31 | }
32 | ```
33 |
34 | something like:
35 |
36 | ```
37 | $ curl http://user:user@localhost:8080/speakers -i -H "Content-type: application/json" -d @yourfile.json`
38 | ```
39 |
40 | where `yourfile.json` is the name of the file holding the new Speaker representation.
41 |
42 | NOTE: Cross-Site Request Forgery support should be disabled locally for the command above
43 | to work, adding `.and().csrf().disable()` should do the trick. Also, `user/user` is the
44 | user that is created in the `SecurityConfig` class.
45 |
46 | === Running the app in the cloud
47 |
48 | If you want to run that exact same app in the cloud, you need first an account with a
49 | cloud provider. At the time of writing, you can try _Pivotal Web Services_ for free for
50 | 60 days, check `https://run.pivotal.io/`
51 |
52 | Once you've registered, you should be able to use `cf t` to confirm you are connected to
53 | the right organization and space.
54 |
55 | Pushing your app to the cloud should be as easy as:
56 |
57 | * Build your app: `mvn package`
58 | * Go in the `target` directory
59 | * Push the app: `cf push my-spring-boot-demo -p spring-boot-intro-conference-0.0.1-SNAPSHOT.jar`
60 |
61 | NOTE: `my-spring-boot-demo` is the name of your app and will also be used for the default
62 | route of your app (i.e. `my-spring-boot-demo.cfapps.io`). If that name is taken, update
63 | the `cf push` command accordingly.
64 |
65 | That's it, your app is in the cloud. You can now send a sample _json_ representation of a
66 | speaker on the service's URL. You can also use `cf logs my-spring-boot-demo` to look at
67 | the logs of the app.
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | org.springframework.boot
8 | spring-boot-starter-parent
9 | 2.2.4.RELEASE
10 |
11 |
12 | com.example
13 | spring-boot-intro-conference
14 | 0.0.1-SNAPSHOT
15 | Spring Boot intro (conference)
16 | Spring Boot introduction sample app
17 |
18 |
19 | 1.8
20 |
21 |
22 |
23 |
24 | org.springframework.boot
25 | spring-boot-starter-web
26 |
27 |
28 | org.springframework.boot
29 | spring-boot-starter-data-jpa
30 |
31 |
32 | org.springframework.boot
33 | spring-boot-starter-data-rest
34 |
35 |
36 | org.springframework.boot
37 | spring-boot-starter-security
38 |
39 |
40 | org.springframework.boot
41 | spring-boot-starter-actuator
42 |
43 |
44 |
45 | com.h2database
46 | h2
47 | runtime
48 |
49 |
50 |
51 | org.springframework.boot
52 | spring-boot-devtools
53 | true
54 |
55 |
56 |
57 | org.springframework.boot
58 | spring-boot-starter-test
59 | test
60 |
61 |
62 |
63 |
64 |
65 |
66 | org.springframework.boot
67 | spring-boot-maven-plugin
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/src/main/java/demo/DemoApplication.java:
--------------------------------------------------------------------------------
1 | package demo;
2 |
3 | import java.util.Random;
4 |
5 | import org.springframework.boot.SpringApplication;
6 | import org.springframework.boot.actuate.health.Health;
7 | import org.springframework.boot.actuate.health.HealthIndicator;
8 | import org.springframework.boot.autoconfigure.SpringBootApplication;
9 | import org.springframework.context.annotation.Bean;
10 |
11 | @SpringBootApplication
12 | public class DemoApplication {
13 |
14 | public static void main(String[] args) {
15 | SpringApplication.run(DemoApplication.class, args);
16 | }
17 |
18 | @Bean
19 | public HealthIndicator myHealthIndicator() {
20 | return () -> {
21 | if (new Random().nextBoolean()) {
22 | return Health.up().build();
23 | }
24 | else {
25 | return Health.down().withDetail("booooooo", 42).build();
26 | }
27 | };
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/demo/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package demo;
2 |
3 | import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
4 | import org.springframework.boot.actuate.health.HealthEndpoint;
5 | import org.springframework.boot.actuate.info.InfoEndpoint;
6 | import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.context.annotation.Configuration;
9 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
10 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
11 | import org.springframework.security.core.userdetails.User;
12 | import org.springframework.security.provisioning.InMemoryUserDetailsManager;
13 |
14 | @Configuration
15 | class SecurityConfig extends WebSecurityConfigurerAdapter {
16 |
17 | @Override
18 | protected void configure(HttpSecurity http) throws Exception {
19 | http.authorizeRequests()
20 | .requestMatchers(EndpointRequest.to(InfoEndpoint.class, HealthEndpoint.class)).permitAll()
21 | .requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("HERO")
22 | .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
23 | .anyRequest().authenticated()
24 | .and().formLogin()
25 | .and().httpBasic();
26 | }
27 |
28 | @Bean
29 | @SuppressWarnings("deprecation")
30 | public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
31 | return new InMemoryUserDetailsManager(
32 | User.withDefaultPasswordEncoder().username("hero").password("hero").roles("HERO", "USER").build(),
33 | User.withDefaultPasswordEncoder().username("user").password("user").roles("USER").build()
34 | );
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/demo/domain/Speaker.java:
--------------------------------------------------------------------------------
1 | package demo.domain;
2 |
3 | import javax.persistence.Column;
4 | import javax.persistence.Entity;
5 | import javax.persistence.GeneratedValue;
6 | import javax.persistence.GenerationType;
7 | import javax.persistence.Id;
8 | import javax.persistence.Lob;
9 |
10 | @Entity
11 | public class Speaker implements java.io.Serializable {
12 |
13 | @GeneratedValue(strategy = GenerationType.IDENTITY)
14 | @Id
15 | private Long id;
16 |
17 | private String firstName;
18 |
19 | private String lastName;
20 |
21 | private String twitter;
22 |
23 | private String github;
24 |
25 | @Column(columnDefinition = "TEXT")
26 | @Lob
27 | private String bio;
28 |
29 | public Speaker() {
30 | }
31 |
32 | public Speaker(String firstName, String lastName, String twitter, String github) {
33 | this.firstName = firstName;
34 | this.lastName = lastName;
35 | this.twitter = twitter;
36 | this.github = github;
37 | }
38 |
39 | public Long getId() {
40 | return id;
41 | }
42 |
43 | public String getFirstName() {
44 | return firstName;
45 | }
46 |
47 | public void setFirstName(String firstName) {
48 | this.firstName = firstName;
49 | }
50 |
51 | public String getLastName() {
52 | return lastName;
53 | }
54 |
55 | public void setLastName(String lastName) {
56 | this.lastName = lastName;
57 | }
58 |
59 | public String getTwitter() {
60 | return twitter;
61 | }
62 |
63 | public void setTwitter(String twitter) {
64 | this.twitter = twitter;
65 | }
66 |
67 | public String getGithub() {
68 | return this.github;
69 | }
70 |
71 | public void setGithub(String github) {
72 | this.github = github;
73 | }
74 |
75 | public String getBio() {
76 | return bio;
77 | }
78 |
79 | public void setBio(String bio) {
80 | this.bio = bio;
81 | }
82 |
83 | @Override
84 | public String toString() {
85 | return "Speaker{" + "id=" + id + ", firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + '}';
86 | }
87 |
88 | }
--------------------------------------------------------------------------------
/src/main/java/demo/domain/SpeakerRepository.java:
--------------------------------------------------------------------------------
1 | package demo.domain;
2 |
3 | import java.util.Collection;
4 |
5 | import org.springframework.data.repository.CrudRepository;
6 | import org.springframework.data.repository.query.Param;
7 | import org.springframework.data.rest.core.annotation.RestResource;
8 |
9 | public interface SpeakerRepository extends CrudRepository {
10 |
11 | @RestResource(path = "by-twitter")
12 | Speaker findByTwitter(@Param("id") String twitter);
13 |
14 | @RestResource(path = "by-github")
15 | Speaker findByGithub(@Param("id") String github);
16 |
17 | Collection findByLastName(@Param("name") String lastName);
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/demo/web/HomeController.java:
--------------------------------------------------------------------------------
1 | package demo.web;
2 |
3 | import org.springframework.web.bind.annotation.GetMapping;
4 | import org.springframework.web.bind.annotation.RestController;
5 |
6 | @RestController
7 | public class HomeController {
8 |
9 | @GetMapping("/")
10 | public String home() {
11 | return "Hello World";
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | management.endpoints.web.exposure.include=*
2 | management.endpoint.health.show-details=when_authorized
--------------------------------------------------------------------------------
/src/main/resources/import.sql:
--------------------------------------------------------------------------------
1 | INSERT INTO SPEAKER(FIRST_NAME, LAST_NAME, TWITTER, GITHUB, BIO) values ('Jürgen', 'Höller', 'springjuergen', 'jhoeller', 'Polishing code since 2002. Spring Framework gatekeeper. Taught Chuck Norris how to code.');
2 | INSERT INTO SPEAKER(FIRST_NAME, LAST_NAME, TWITTER, GITHUB, BIO) values ('Dave', 'Syer', 'david_syer', 'dsyer', 'Founder and contributor to Spring Batch, lead of Spring Security OAuth, and an active contributor to Spring Integration, Spring Framework, Spring AMQP, Spring Security. Experienced, delivery-focused architect and development manager. Has designed and built successful enterprise software solutions using Spring, and implemented them in major institutions worldwide.');
3 | INSERT INTO SPEAKER(FIRST_NAME, LAST_NAME, TWITTER, GITHUB, BIO) values ('Andy', 'Wilkinson', 'ankinson', 'wilkinsona', 'Husband. Dad. Fair weather mountain biker. Developer at Pivotal/SpringSource by day, iOS developer @deftmethods by night.');
4 | INSERT INTO SPEAKER(FIRST_NAME, LAST_NAME, TWITTER, GITHUB, BIO) values ('Phil', 'Webb', 'phillip_webb', 'philwebb', 'Phil Webb is a Spring Framework developer and co-creator of the Spring Boot project. Prior to joining Pivotal and relocating to California, Phil worked for a number of UK technology companies.');
5 | INSERT INTO SPEAKER(FIRST_NAME, LAST_NAME, TWITTER, GITHUB, BIO) values ('Madhura', 'Bhave', 'madhurabhave23', 'mbhave', 'Spring Boot Developer');
6 | INSERT INTO SPEAKER(FIRST_NAME, LAST_NAME, TWITTER, GITHUB, BIO) values ('Brian', 'Clozel', 'bclozel', 'bclozel', 'Spring Framework committer, Pivotal inc.');
7 | INSERT INTO SPEAKER(FIRST_NAME, LAST_NAME, TWITTER, GITHUB, BIO) values ('Stéphane', 'Nicoll', 'snicoll', 'snicoll', 'Stéphane has a thing for code quality and robustness. He''s been spreading the word for more than ten years while developing large scale Java enterprise applications in the geospatial, financial, or logistics sectors. An Apache Maven PMC member since 2006, he joined the core Spring Framework development team early 2014, being one of the main contributors to both Spring Framework and Spring Boot since. During his free time, he loves traveling around the world.');
8 | INSERT INTO SPEAKER(FIRST_NAME, LAST_NAME, TWITTER, GITHUB, BIO) values ('Sébastien', 'Deleuze', 'sdeleuze', 'sdeleuze', 'Spring framework commiter @ Pivotal and Dart Google Developer Expert');
9 | INSERT INTO SPEAKER(FIRST_NAME, LAST_NAME, TWITTER, GITHUB, BIO) values ('Yann', 'Cébron', 'yanncebron', 'YannCebron', 'Yann has been working on Spring support in IntelliJ IDEA for three years. Before joining JetBrains, he spent a decade on (web) applications for a large variety of customers and trades. When he’s not coding, you’ll probably find him in the woods walking his dog or playing the piano.');
10 | INSERT INTO SPEAKER(FIRST_NAME, LAST_NAME, TWITTER, GITHUB, BIO) values ('Trisha', 'Gee', 'trisha_gee', 'trishagee', 'Trisha has developed Java applications for a range of industries, including finance, manufacturing and non-profit, for companies of all sizes. She has expertise in Java high performance systems, is passionate about enabling developer productivity, and dabbles with Open Source development. Trisha blogs regularly on subjects that she thinks developers and other humans should care about, she’s a leader of the Sevilla Java User Group, a key member of the London Java Community and a Java Champion - she believes we shouldn''t all have to make the same mistakes again and again.');
11 | INSERT INTO SPEAKER(FIRST_NAME, LAST_NAME, TWITTER, GITHUB, BIO) values ('Mark', 'Fisher', 'm_f_', 'markfisher', 'Mark has been a member of the Spring team for over 8 years, contributing to the Spring Framework and several other projects. He founded Spring Integration in 2007 and is one of the authors of Spring Integration in Action, published by Manning in 2012. Currently he co-leads Spring XD where he focuses on the distributed runtime and everything related to the flow of data.');
12 | INSERT INTO SPEAKER(FIRST_NAME, LAST_NAME, TWITTER, GITHUB, BIO) values ('Gary', 'Russel', 'gprussel', 'garyrussell', 'Gary has been in software engineering, concentrating on Enterprise Integration, for over 30 years on various platforms, and in the Java space since the late ''90s. He has been developing with the Spring Framework since 2004. He has been a committer on the Spring Integration and Spring AMQP projects for over 4 years, and now leads both projects.');
13 | INSERT INTO SPEAKER(FIRST_NAME, LAST_NAME, TWITTER, GITHUB, BIO) values ('Christoph', 'Strobl', 'stroblchristoph', 'christophstrobl', 'father, coder,... with yet so much to learn having so little time');
14 | INSERT INTO SPEAKER(FIRST_NAME, LAST_NAME, TWITTER, GITHUB, BIO) values ('Mark', 'Paluch', 'mp911de', 'mp911de', 'Mark is Software Craftsman, Spring Data Engineer at Pivotal, a member of the CDI 2.0 expert group, and Lead of the lettuce Redis driver. He has been into developing Java server-, frontend-, and web applications for over 12 years and his focus is now on software architecture, Spring and Redis clients.');
15 |
--------------------------------------------------------------------------------
/src/main/resources/static/error/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Ooops, page not found
6 |
16 |
17 |
18 |
19 |
20 |
¯\_(ツ)_/¯
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------