Merge branch 'master' of github.com:OtusTeam/Spring

This commit is contained in:
Yuriy Dvorzhetskiy
2021-08-25 22:55:05 +06:00
89 changed files with 2878 additions and 11 deletions
@@ -20,11 +20,6 @@
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
@@ -20,11 +20,6 @@
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
@@ -0,0 +1,24 @@
target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/build/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>spring-mvc-view-class-work</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<modules>
<module>spring-mvc-view-exercise</module>
<module>spring-mvc-view-solution</module>
</modules>
</project>
@@ -0,0 +1,24 @@
target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/build/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>spring-mvc-view-exercise</artifactId>
<version>1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
</parent>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,28 @@
package ru.otus.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import ru.otus.spring.domain.Person;
import ru.otus.spring.repostory.PersonRepository;
import javax.annotation.PostConstruct;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class);
}
//Чтобы не усложнять пример, делать так нельзя :)
@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
@Autowired
private PersonRepository repository;
@PostConstruct
public void init() {
repository.save(new Person("Pushkin"));
repository.save(new Person("Lermontov"));
}
}
@@ -0,0 +1,37 @@
package ru.otus.spring.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Person {
@Id
@GeneratedValue
private int id;
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@@ -0,0 +1,11 @@
package ru.otus.spring.repostory;
import org.springframework.data.repository.CrudRepository;
import ru.otus.spring.domain.Person;
import java.util.List;
public interface PersonRepository extends CrudRepository<Person, Integer> {
List<Person> findAll();
}
@@ -0,0 +1,7 @@
package ru.otus.spring.rest;
class NotFoundException extends RuntimeException{
NotFoundException() {
}
}
@@ -0,0 +1,35 @@
package ru.otus.spring.rest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import ru.otus.spring.domain.Person;
import ru.otus.spring.repostory.PersonRepository;
import java.util.List;
import java.util.stream.Collectors;
@Controller
public class PersonController {
private final PersonRepository repository;
@Autowired
public PersonController(PersonRepository repository) {
this.repository = repository;
}
@GetMapping("/")
public String listPage(Model model) {
List<Person> persons = repository.findAll();
model.addAttribute("persons", persons);
return "list";
}
@GetMapping("/edit")
public String editPage(@RequestParam("id") int id, Model model) {
return null;
}
}
@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Edit person</title>
<style type="text/css">
body {
padding: 50px;
}
label {
display: inline-block;
width: 100px;
}
input:read-only {
background: lightgray;
}
.row {
margin-top: 10px;
}
</style>
</head>
<body>
<!-- Person edition -->
<form id="edit-form" action="edit.html">
<h1>Person Info:</h1>
<div class="row">
<label for="id-input">ID:</label>
<input id="id-input" type="text" readonly="readonly" value="1"/>
</div>
<div class="row">
<label for="holder-input">Name:</label>
<input id="holder-input" name="name" type="text" value="John Doe"/>
</div>
<div class="row">
<button type="submit">Save</button>
</div>
</form>
</body>
</html>
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>List of all persons</title>
<style type="text/css">
body {
padding: 50px;
}
.persons, .persons td {
border: 1px solid lightgray;
padding: 5px;
}
</style>
</head>
<body>
<h1>Persons:</h1>
<table class="persons">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>John Doe</td>
<td>
<a href="edit.html">Edit</a>
</td>
</tr>
</tbody>
</table>
</body>
</html>
@@ -0,0 +1,24 @@
target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/build/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>spring-mvc-view-solution</artifactId>
<version>1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
</parent>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,27 @@
package ru.otus.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import ru.otus.spring.domain.Person;
import ru.otus.spring.repostory.PersonRepository;
import javax.annotation.PostConstruct;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class);
}
@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
@Autowired
private PersonRepository repository;
@PostConstruct
public void init() {
repository.save(new Person("Pushkin"));
repository.save(new Person("Lermontov"));
}
}
@@ -0,0 +1,37 @@
package ru.otus.spring.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Person {
@Id
@GeneratedValue
private int id;
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@@ -0,0 +1,11 @@
package ru.otus.spring.repostory;
import org.springframework.data.repository.CrudRepository;
import ru.otus.spring.domain.Person;
import java.util.List;
public interface PersonRepository extends CrudRepository<Person, Integer> {
List<Person> findAll();
}
@@ -0,0 +1,7 @@
package ru.otus.spring.rest;
class NotFoundException extends RuntimeException{
NotFoundException() {
}
}
@@ -0,0 +1,52 @@
package ru.otus.spring.rest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import ru.otus.spring.domain.Person;
import ru.otus.spring.repostory.PersonRepository;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Controller
public class PersonController {
private final PersonRepository repository;
@Autowired
public PersonController(PersonRepository repository) {
this.repository = repository;
}
@GetMapping("/")
public String listPage(Model model) {
List<Person> persons = repository.findAll();
model.addAttribute("persons", persons);
return "list";
}
@GetMapping("/edit")
public String editPage(@RequestParam("id") int id, Model model) {
Person person = repository.findById(id).orElseThrow(NotFoundException::new);
model.addAttribute("person", person);
return "edit";
}
@PostMapping("/edit")
public String savePerson(
Person person,
Model model
) {
Person saved = repository.save(person);
model.addAttribute(saved);
//Ошибка! Нужен редирект!
return "edit";
}
}
@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Edit person</title>
<style type="text/css">
body {
padding: 50px;
}
label {
display: inline-block;
width: 100px;
}
input:read-only {
background: lightgray;
}
.row {
margin-top: 10px;
}
</style>
</head>
<body>
<!-- Person edition -->
<form id="edit-form" th:action="@{/edit(id=${person.id})}" th:method="post" action="edit.html">
<h1>Person Info:</h1>
<div class="row">
<label for="id-input">ID:</label>
<input id="id-input" type="text" readonly="readonly" th:value="${person.id}" value="1"/>
</div>
<div class="row">
<label for="holder-input">Name:</label>
<input id="holder-input" name="name" type="text" th:value="${person.name}" value="John Doe"/>
</div>
<div class="row">
<button type="submit">Save</button>
</div>
</form>
</body>
</html>
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>List of all persons</title>
<style type="text/css">
body {
padding: 50px;
}
.persons, .persons td {
border: 1px solid lightgray;
padding: 5px;
}
</style>
</head>
<body>
<h1>Persons:</h1>
<table class="persons">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr th:each="person : ${persons}">
<td th:text="${person.id}">1</td>
<td th:text="${person.name}">John Doe</td>
<td>
<a th:href="@{/edit(id=${person.id})}" href="edit.html">Edit</a>
</td>
</tr>
</tbody>
</table>
</body>
</html>
@@ -0,0 +1,30 @@
target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/build/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
node/
/node_modules
/output
package-lock.json
@@ -0,0 +1,26 @@
{
"name": "client",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "webpack-dev-server --config webpack.dev.config.js",
"build": "webpack"
},
"author": "",
"license": "ISC",
"dependencies": {
"react": "^16.2.0",
"react-dom": "^16.2.0"
},
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"html-webpack-plugin": "^3.2.0",
"webpack": "^3.10.0",
"webpack-cli": "^3.2.3",
"webpack-dev-server": "^2.9.7"
}
}
@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>spring-17-boot-and-react</artifactId>
<version>1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
</parent>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>install node and npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<nodeVersion>v10.15.1</nodeVersion>
<npmVersion>6.4.1</npmVersion>
</configuration>
</execution>
<execution>
<id>npm install</id>
<goals>
<goal>npm</goal>
</goals>
<phase>generate-resources</phase>
<configuration>
<arguments>install</arguments>
</configuration>
</execution>
<execution>
<id>npm run build</id>
<goals>
<goal>npm</goal>
</goals>
<phase>generate-resources</phase>
<configuration>
<arguments>run build</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,27 @@
package ru.otus.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import ru.otus.spring.domain.Person;
import ru.otus.spring.repostory.PersonRepository;
import javax.annotation.PostConstruct;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class);
}
@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
@Autowired
private PersonRepository repository;
@PostConstruct
public void init() {
repository.save(new Person("Pushkin"));
repository.save(new Person("Lermontov"));
}
}
@@ -0,0 +1,37 @@
package ru.otus.spring.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Person {
@Id
@GeneratedValue
private int id;
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@@ -0,0 +1,11 @@
package ru.otus.spring.repostory;
import org.springframework.data.repository.CrudRepository;
import ru.otus.spring.domain.Person;
import java.util.List;
public interface PersonRepository extends CrudRepository<Person, Integer> {
List<Person> findAll();
}
@@ -0,0 +1,30 @@
package ru.otus.spring.rest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import ru.otus.spring.domain.Person;
import ru.otus.spring.repostory.PersonRepository;
import ru.otus.spring.rest.dto.PersonDto;
import java.util.List;
import java.util.stream.Collectors;
@RestController
public class PersonController {
private final PersonRepository repository;
@Autowired
public PersonController(PersonRepository repository) {
this.repository = repository;
}
@GetMapping("/api/persons")
public List<PersonDto> getAllPersons() {
return repository.findAll().stream().map(PersonDto::toDto)
.collect(Collectors.toList());
}
}
@@ -0,0 +1,50 @@
/*
* Copyright 2016 Russian Post
*
* This source code is Russian Post Confidential Proprietary.
* This software is protected by copyright. All rights and titles are reserved.
* You shall not use, copy, distribute, modify, decompile, disassemble or reverse engineer the software.
* Otherwise this violation would be treated by law and would be subject to legal prosecution.
* Legal use of the software provides receipt of a license from the right name only.
*/
package ru.otus.spring.rest.dto;
import ru.otus.spring.domain.Person;
/**
* DTO that represents Account
*/
@SuppressWarnings("all")
public class PersonDto {
private int id = -1;
private String name;
public PersonDto() {
}
public PersonDto(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static PersonDto toDto(Person person) {
return new PersonDto(person.getId(), person.getName());
}
}
@@ -0,0 +1,45 @@
import React from 'react'
const Header = (props) => (
<h1>{props.title}</h1>
);
export default class App extends React.Component {
constructor() {
super();
this.state = {persons: []};
}
componentDidMount() {
fetch('/api/persons')
.then(response => response.json())
.then(persons => this.setState({persons}));
}
render() {
return (
<React.Fragment>
<Header title={'Persons'}/>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
</tr>
</thead>
<tbody>
{
this.state.persons.map((person, i) => (
<tr key={i}>
<td>{person.id}</td>
<td>{person.name}</td>
</tr>
))
}
</tbody>
</table>
</React.Fragment>
)
}
};
@@ -0,0 +1,15 @@
<!doctype html>
<html lang="en">
<head>
<title>Minimal React Boilerplate</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<script src="./bundle.js" type="text/javascript"></script>
</body>
</html>
@@ -0,0 +1,9 @@
import React from 'react'
import ReactDOM from 'react-dom'
import App from './components/App'
ReactDOM.render(
<App />,
document.getElementById('root')
)
@@ -0,0 +1,47 @@
const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: './src/ui/index.js',
output: {
path: path.resolve(__dirname, 'target/classes/public/'),
filename: 'bundle.min.js',
libraryTarget: 'umd'
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components|build)/,
use: {
loader: 'babel-loader',
options: {
presets: ['env', 'react']
}
}
}
]
},
plugins: [
new webpack.DefinePlugin({
"process.env": {
NODE_ENV: JSON.stringify("production")
}
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
},
output: {
comments: false,
},
}),
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'src/ui/index.html'
})
]
}
@@ -0,0 +1,46 @@
const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require('path');
module.exports = {
entry: './src/ui/index.js',
devtool: 'inline-source-map',
output: {
path: path.resolve(__dirname),
filename: 'bundle.js',
libraryTarget: 'umd'
},
devServer: {
contentBase: path.resolve(__dirname) + '/src/ui',
compress: true,
port: 9000,
host: 'localhost',
open: true,
before: (app) => {
app.get('/api/persons', (req, res) => res.send([
{id: '1', name: 'Привяу'}
]));
}
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components|build)/,
use: {
loader: 'babel-loader',
options: {
presets: ['env', 'react']
}
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'src/ui/index.html'
})
]
}
@@ -0,0 +1,24 @@
target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/build/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>spring-17-jquery</artifactId>
<version>1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
</parent>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,27 @@
package ru.otus.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import ru.otus.spring.domain.Person;
import ru.otus.spring.repostory.PersonRepository;
import javax.annotation.PostConstruct;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class);
}
@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
@Autowired
private PersonRepository repository;
@PostConstruct
public void init() {
repository.save(new Person("Pushkin"));
repository.save(new Person("Lermontov"));
}
}
@@ -0,0 +1,37 @@
package ru.otus.spring.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Person {
@Id
@GeneratedValue
private int id;
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@@ -0,0 +1,15 @@
package ru.otus.spring.page;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class PersonPagesController {
@GetMapping("/")
public String listPage(Model model) {
model.addAttribute("keywords", "list users in Omsk, omsk, list users, list users free");
return "list";
}
}
@@ -0,0 +1,11 @@
package ru.otus.spring.repostory;
import org.springframework.data.repository.CrudRepository;
import ru.otus.spring.domain.Person;
import java.util.List;
public interface PersonRepository extends CrudRepository<Person, Integer> {
List<Person> findAll();
}
@@ -0,0 +1,30 @@
package ru.otus.spring.rest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import ru.otus.spring.domain.Person;
import ru.otus.spring.repostory.PersonRepository;
import ru.otus.spring.rest.dto.PersonDto;
import java.util.List;
import java.util.stream.Collectors;
@RestController
public class PersonController {
private final PersonRepository repository;
@Autowired
public PersonController(PersonRepository repository) {
this.repository = repository;
}
@GetMapping("/api/persons")
public List<PersonDto> getAllPersons() {
return repository.findAll().stream().map(PersonDto::toDto)
.collect(Collectors.toList());
}
}
@@ -0,0 +1,50 @@
/*
* Copyright 2016 Russian Post
*
* This source code is Russian Post Confidential Proprietary.
* This software is protected by copyright. All rights and titles are reserved.
* You shall not use, copy, distribute, modify, decompile, disassemble or reverse engineer the software.
* Otherwise this violation would be treated by law and would be subject to legal prosecution.
* Legal use of the software provides receipt of a license from the right name only.
*/
package ru.otus.spring.rest.dto;
import ru.otus.spring.domain.Person;
/**
* DTO that represents Account
*/
@SuppressWarnings("all")
public class PersonDto {
private int id = -1;
private String name;
public PersonDto() {
}
public PersonDto(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static PersonDto toDto(Person person) {
return new PersonDto(person.getId(), person.getName());
}
}
@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<meta name="keywords" th:content="${keywords}"/>
<title>List of all persons</title>
<style type="text/css">
body {
padding: 50px;
}
.persons, .persons td {
border: 1px solid lightgray;
padding: 5px;
}
</style>
<script src="/webjars/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<h1>Persons:</h1>
<table class="persons">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<script>
$(function () {
$.get('/api/persons').done(function (persons) {
persons.forEach(function (person) {
$('tbody').append(`
<tr>
<td>${person.id}</td>
<td>${person.name}</td>
</tr>
`)
});
})
});
</script>
</body>
</html>
+7
View File
@@ -0,0 +1,7 @@
.idea/
*.iml
target/
/node_modules
/output
+33
View File
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>spring-18</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
</parent>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>io.reactivex.rxjava2</groupId>
<artifactId>rxjava</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.1-jre</version>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,35 @@
package ru.otus;
import io.reactivex.Observable;
@SuppressWarnings("ResultOfMethodCallIgnored")
public class CreateExamples {
public static void main(String[] args) {
Observable<String> obs = justExample();
obs.forEach(System.out::println);
obs.forEach(System.out::println);
}
public static Observable<String> justExample() {
return Observable.just("one", "two", "three");
}
public static Observable<String> createExample() {
return Observable.create(emitter -> {
if (emitter.isDisposed()) {
return;
}
emitter.onNext("one");
emitter.onNext("two");//!
emitter.onNext("three");
if (!emitter.isDisposed()) {
emitter.onComplete();
}
});
}
public static Observable<String> deferExample() {
return Observable.defer(() -> Observable.just("one", "two", "three"));
}
}
@@ -0,0 +1,26 @@
package ru.otus;
import io.reactivex.Observable;
import java.io.IOException;
public class LiveLikeExample {
public static void main(String[] args) throws IOException {
System.in.read();
}
static Observable<String> getName() {
return Observable.just("Jake");
}
static Observable<String> getSurname() {
return Observable.just("Foo");
}
static Observable<String> save(String fullName) {
System.out.println(fullName + " saved!");
return Observable.just("OK!");
}
}
@@ -0,0 +1,79 @@
package ru.otus;
import com.google.common.collect.ImmutableList;
import io.reactivex.Observable;
import io.reactivex.ObservableTransformer;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.PublishSubject;
import java.time.LocalDate;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@SuppressWarnings("ResultOfMethodCallIgnored")
public class OperatorsExample {
public static void main(String[] args) throws Exception {
simpleExample();
System.in.read();
}
public static void simpleExample() throws Exception {
List<Person> persons = ImmutableList.of(
new Person("John", "Dow", "male", LocalDate.of(1992, 3, 12)),
new Person("Jane", "Dow", "female", LocalDate.of(2001, 6, 23)),
new Person("Howard", "Lovecraft", "male", LocalDate.of(1890, 8, 20)),
new Person("Joanne", "Rowling", "female", LocalDate.of(1965, 6, 30)));
Observable.fromIterable(persons)
.filter(
person -> person.getBirth().isAfter(LocalDate.of(1990, 1, 1))
)
.map(p -> p.getFirstName() + " " + p.getLastName())
.toList()
.subscribe(System.out::println);
}
public static void publisherExample() throws Exception {
final Observable<String> ob = magicPublisher();
System.out.println("First subscribed");
ob.subscribe(System.out::println);
Thread.sleep(5000);
System.out.println("Second subscribed");
ob.subscribe(System.out::println);
}
public static Observable<String> magicPublisher() {
Random r = new Random(1);
AtomicInteger i = new AtomicInteger();
final Observable<String> obs = Observable.<String>generate(emitter ->
emitter.onNext("" + i.incrementAndGet()))
.concatMap(s -> Observable.just(s).delay(r.nextInt(1000), TimeUnit.MILLISECONDS))
.subscribeOn(Schedulers.newThread());
PublishSubject<String> subject = PublishSubject.create();
// BehaviorSubject<String> subject = BehaviorSubject.create();
// AsyncSubject<String> subject = AsyncSubject.create();
// CompletableFuture.runAsync(() -> {
// try {
// Thread.sleep(7000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// subject.onComplete();
// });
// ReplaySubject<String> subject = ReplaySubject.create();
obs.subscribe(subject);
return subject;
}
//composeExmaple
private static ObservableTransformer<String, String> filterAndUpperCase() {
return upstream -> upstream
.filter(s -> s.length() >= 4)
.map(String::toUpperCase);
}
}
@@ -0,0 +1,66 @@
package ru.otus;
import java.time.LocalDate;
import java.util.Objects;
public class Person {
private String firstName;
private String lastName;
private String gender;
private LocalDate birth;
public Person(String firstName, String lastName, String gender, LocalDate birth) {
this.firstName = firstName;
this.lastName = lastName;
this.gender = gender;
this.birth = birth;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public LocalDate getBirth() {
return birth;
}
public void setBirth(LocalDate birth) {
this.birth = birth;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(firstName, person.firstName) &&
Objects.equals(lastName, person.lastName) &&
Objects.equals(gender, person.gender) &&
Objects.equals(birth, person.birth);
}
@Override
public int hashCode() {
return Objects.hash(firstName, lastName, gender, birth);
}
}
@@ -0,0 +1,40 @@
package ru.otus.comparison;
import io.reactivex.Observable;
import io.reactivex.schedulers.Schedulers;
import java.io.IOException;
public class AsyncComparison {
public static void main(String[] args) throws IOException {
final long timeStarted = System.currentTimeMillis();
final Observable<String> obs = controller();
obs.subscribe(System.out::println);
System.out.println("Wait time " + (System.currentTimeMillis() - timeStarted));
System.in.read();
}
static Observable<String> controller() {
return service();
}
static Observable<String> service() {
return repository();
}
static Observable<String> repository() {
return database();
}
static Observable<String> database() {
return Observable.defer(() -> {
try {
Thread.sleep(4000);
} catch (Exception e) {
System.out.println("Don't do this");
}
return Observable.just("Hello world");
}).subscribeOn(Schedulers.newThread());
}
}
@@ -0,0 +1,31 @@
package ru.otus.comparison;
public class SyncComparison {
public static void main(String[] args) {
final long timeStarted = System.currentTimeMillis();
System.out.println(controller());
System.out.println(System.currentTimeMillis() - timeStarted);
}
static String controller() {
return service();
}
static String service() {
return repository();
}
static String repository() {
return database();
}
static String database() {
try {
Thread.sleep(4000);
} catch (Exception e) {
System.out.println("Don't do this");
}
return "Hello world";
}
}
+24
View File
@@ -0,0 +1,24 @@
target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/build/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>spring-19</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<modules>
<module>spring-19-web-flux</module>
<module>spring-19-reactor</module>
<module>spring-19-reactive-spring-data</module>
</modules>
</project>
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>spring-19-reactive-spring-data</artifactId>
<version>1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.target>13</maven.compiler.target>
<maven.compiler.source>13</maven.compiler.source>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-reactivestreams</artifactId>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,20 @@
package ru.otus.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import ru.otus.spring.repostory.AccountRepository;
import ru.otus.spring.repostory.PersonRepository;
@SpringBootApplication
public class Main {
public static void main(String[] args) throws InterruptedException {
ApplicationContext context = SpringApplication.run(Main.class);
PersonRepository repository = context.getBean(PersonRepository.class);
AccountRepository accountRepository = context.getBean(AccountRepository.class);
Thread.sleep(20000);
}
}
@@ -0,0 +1,37 @@
package ru.otus.spring.domain;
public class Account {
private String id;
private String personId;
private Long amount;
public Account(String id, String personId, Long amount) {
this.id = id;
this.personId = personId;
this.amount = amount;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPersonId() {
return personId;
}
public void setPersonId(String personId) {
this.personId = personId;
}
public Long getAmount() {
return amount;
}
public void setAmount(Long amount) {
this.amount = amount;
}
}
@@ -0,0 +1,27 @@
package ru.otus.spring.domain;
public class Person {
private String id;
private String name;
public Person(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@@ -0,0 +1,8 @@
package ru.otus.spring.repostory;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import ru.otus.spring.domain.Account;
import ru.otus.spring.domain.Person;
public interface AccountRepository extends ReactiveMongoRepository<Account, String> {
}
@@ -0,0 +1,15 @@
package ru.otus.spring.repostory;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import ru.otus.spring.domain.Person;
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
Flux<Person> findByName(String name);
@Query("{ 'name': ?0 }")
Mono<Person> findFirstByName(String name);
}
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>spring-19-reactor</artifactId>
<version>1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.target>13</maven.compiler.target>
<maven.compiler.source>13</maven.compiler.source>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,20 @@
package ru.otus.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import ru.otus.spring.reactor.FluxService;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Main.class);
FluxService service = context.getBean(FluxService.class);
service.printHello("Ivan");
}
}
@@ -0,0 +1,50 @@
package ru.otus.spring.reactor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.Disposable;
import reactor.core.publisher.DirectProcessor;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Service
public class FluxService {
private final Logger logger = LoggerFactory.getLogger(FluxService.class);
private final NonFluxService nonFluxService;
private final DirectProcessor<Message> processor;
private final Disposable flow;
@Autowired
public FluxService(NonFluxService nonFluxService) {
this.nonFluxService = nonFluxService;
// Создаём процессор - это reactor-овская реализация reactive-stream интерфейса
// Direct processor, кстати - это простой последовательный вызов методов)
processor = DirectProcessor.create();
// Здесь мы настриваем flow
flow = Mono.from(processor)
.map(nonFluxService::nonFluxSayHello)
.subscribe(this::printMessage);
}
/**
* Этот метод будет инициировать асинзронную обрабтку сообщения
*
* @param name это имя будет приходить из не-reactor окружения
*/
public void printHello(String name) {
processor.onNext(new Message(name));
}
/**
* А это терминальный шаг для сообщения
*
* @param message а это финальный шаг для сообщения, отсюда можно вернуть рзультат в не-реактив окружение
*/
private void printMessage(Message message) {
logger.info("Message received: {}", message.getValue());
}
}
@@ -0,0 +1,14 @@
package ru.otus.spring.reactor;
public class Message {
private final String value;
public Message(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
@@ -0,0 +1,24 @@
package ru.otus.spring.reactor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class NonFluxService {
private final Logger logger = LoggerFactory.getLogger(NonFluxService.class);
public Message nonFluxSayHello(Message message) {
logger.info("Message received in non-flux service: {}", message.getValue());
final String name = message.getValue();
final String withHello = "Hello, " + name + "!";
try {
Thread.sleep(1000);
return new Message(withHello);
} catch (InterruptedException ex) {
return new Message(withHello);
}
}
}
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>spring-19-web-flux</artifactId>
<version>1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.target>13</maven.compiler.target>
<maven.compiler.source>13</maven.compiler.source>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- работает и без неё, но нужна нам, для RxJava методов -->
<dependency>
<groupId>io.reactivex.rxjava2</groupId>
<artifactId>rxjava</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,14 @@
package ru.otus.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class);
}
}
@@ -0,0 +1,33 @@
package ru.otus.spring;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
@RestController
public class ReactorController {
@GetMapping("/flux/one")
public Mono<String> one() {
return Mono.just("one");
}
@GetMapping("/flux/ten")
public Flux<Integer> list() {
return Flux.range(1, 10);
}
@GetMapping(path = "/flux/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream() {
return Flux.generate(() -> 0, (state, emitter) -> {
emitter.next(state);
return state + 1;
})
.delayElements(Duration.ofSeconds(1L))
.map(Object::toString);
}
}
@@ -0,0 +1,20 @@
package ru.otus.spring;
import io.reactivex.Flowable;
import io.reactivex.Single;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RxJava2Controller {
@GetMapping("/rx/one")
public Single<String> single() {
return Single.just("one");
}
@GetMapping("/rx/ten")
public Flowable<Integer> list() {
return Flowable.range(1, 10);
}
}
+4
View File
@@ -0,0 +1,4 @@
.idea/
*.iml
target/
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>spring-20</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<modules>
<module>spring-20-exercise</module>
<module>spring-20-solution</module>
</modules>
</project>
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>spring-20-exercise</artifactId>
<version>1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!-- Зависимости WebFlux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- Зависимости Reactive SpringData -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
</dependency>
<!-- Тестирование -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,69 @@
package ru.otus.spring;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import ru.otus.spring.domain.Person;
import ru.otus.spring.repository.PersonRepository;
import java.util.Arrays;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.BodyInserters.fromValue;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RequestPredicates.queryParam;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
import static org.springframework.web.reactive.function.server.ServerResponse.*;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Main.class);
PersonRepository repository = context.getBean(PersonRepository.class);
repository.saveAll(Arrays.asList(
new Person("Pushkin", 22),
new Person("Lermontov", 22),
new Person("Tolstoy", 60)
)).subscribe(p -> System.out.println(p.getLastName()));
}
@Bean
public RouterFunction<ServerResponse> composedRoutes(PersonRepository repository) {
return route()
// Обратите внимание на использование хэндлера
.GET("/func/person", accept(APPLICATION_JSON), new PersonHandler(repository)::list)
// Обратите внимание на использование pathVariable
.GET("/func/person/{id}", accept(APPLICATION_JSON),
request -> repository.findById(request.pathVariable("id"))
.flatMap(person -> ok().contentType(APPLICATION_JSON).body(fromValue(person)))
.switchIfEmpty(notFound().build())
).build();
}
// Это пример хэндлера, который даже не бин
static class PersonHandler {
private final PersonRepository repository;
PersonHandler(PersonRepository repository) {
this.repository = repository;
}
Mono<ServerResponse> list(ServerRequest request) {
// Обратите внимание на пример другого порядка создания response от Flux
return ok().contentType(APPLICATION_JSON).body(repository.findAll(), Person.class);
}
}
}
@@ -0,0 +1,55 @@
package ru.otus.spring.domain;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
@Document
public class Person {
@Id
private String id;
@JsonProperty("name")
@Field("name")
private String lastName;
private int age;
public Person() {
}
public Person(String lastName) {
this.lastName = lastName;
}
public Person(String lastName, int age) {
this.lastName = lastName;
this.age = age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
@@ -0,0 +1,20 @@
package ru.otus.spring.repository;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import ru.otus.spring.domain.Person;
public interface PersonRepository
extends ReactiveMongoRepository<Person, String> {
Flux<Person> findAll();
Mono<Person> findById(String id);
Mono<Person> save(Mono<Person> person);
Flux<Person> findAllByLastName(String lastName);
Flux<Person> findAllByAge(int age);
}
@@ -0,0 +1,33 @@
package ru.otus.spring.rest;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
@RestController
public class AnnotatedController {
@GetMapping("/flux/one")
public Mono<String> one() {
return Mono.just("one");
}
@GetMapping("/flux/ten")
public Flux<Integer> list() {
return Flux.range(1, 10).delayElements(Duration.ofSeconds(1));
}
@GetMapping(path = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream() {
return Flux.generate(() -> 0, (state, emitter) -> {
emitter.next(state);
return state + 1;
})
.delayElements(Duration.ofSeconds(1L))
.map(i -> "" + i);
}
}
@@ -0,0 +1,37 @@
package ru.otus.spring.rest;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import ru.otus.spring.domain.Person;
import ru.otus.spring.repository.PersonRepository;
@RestController
public class PersonController {
private final PersonRepository repository;
public PersonController(PersonRepository repository) {
this.repository = repository;
}
@GetMapping("/person")
public Flux<Person> all() {
return repository.findAll();
}
@GetMapping("/person/{id}")
public Mono<Person> byId(@PathVariable("id") String id) {
return repository.findById(id);
}
@PostMapping("/person")
public Mono<Person> save(@RequestBody Mono<Person> dto) {
return repository.save(dto);
}
@GetMapping("/person/find")
public Flux<Person> byName(@RequestParam("name") String name) {
return repository.findAllByLastName(name);
}
}
@@ -0,0 +1,28 @@
package ru.otus.spring.repository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import ru.otus.spring.domain.Person;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@DataMongoTest
public class PersonRepositoryTest {
@Autowired
private PersonRepository repository;
@Test
public void shouldSetIdOnSave() {
Mono<Person> personMono = repository.save(new Person("Bill", 12));
StepVerifier
.create(personMono)
.assertNext(person -> assertNotNull(person.getId()))
.expectComplete()
.verify();
}
}
@@ -0,0 +1,28 @@
package ru.otus.spring.rest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
@SpringBootTest
public class PersonControllerTest {
@Autowired
private RouterFunction<ServerResponse> route;
@Test
public void testRoute() {
WebTestClient client = WebTestClient
.bindToRouterFunction(route)
.build();
client.get()
.uri("/func/person")
.exchange()
.expectStatus()
.isOk();
}
}
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.otus</groupId>
<artifactId>spring-20-solution</artifactId>
<version>1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!-- Зависимости WebFlux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- Зависимости Reactive SpringData -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
</dependency>
<!-- Тестирование -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,82 @@
package ru.otus.spring;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import ru.otus.spring.domain.Person;
import ru.otus.spring.repository.PersonRepository;
import java.util.Arrays;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.BodyInserters.fromValue;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RequestPredicates.queryParam;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
import static org.springframework.web.reactive.function.server.ServerResponse.*;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Main.class);
PersonRepository repository = context.getBean(PersonRepository.class);
repository.saveAll(Arrays.asList(
new Person("Pushkin", 22),
new Person("Lermontov", 22),
new Person("Tolstoy", 60)
)).subscribe(p -> System.out.println(p.getLastName()));
}
@Bean
public RouterFunction<ServerResponse> composedRoutes(PersonRepository repository) {
return route()
// эта функция должна стоять раньше findAll - порядок следования роутов - важен
.GET("/func/person", queryParam("name", StringUtils::isNotEmpty),
request -> request.queryParam("name")
.map(repository::findAllByLastName)
.map(persons -> ok().body(persons, Person.class))
.orElse(badRequest().build())
)
// пример другой реализации - начиная с запроса репозитория
.GET("/func/person", queryParam("age", StringUtils::isNotEmpty),
req -> repository.findAllByLastName(
req.queryParam("age").orElseThrow(IllegalArgumentException::new)
)
.collectList()
.flatMap(persons -> ok().body(persons, Person.class)))
// Обратите внимание на использование хэндлера
.GET("/func/person", accept(APPLICATION_JSON), new PersonHandler(repository)::list)
// Обратите внимание на использование pathVariable
.GET("/func/person/{id}", accept(APPLICATION_JSON),
request -> repository.findById(request.pathVariable("id"))
.flatMap(person -> ok().contentType(APPLICATION_JSON).body(fromValue(person)))
.switchIfEmpty(notFound().build())
).build();
}
// Это пример хэндлера, который даже не бин
static class PersonHandler {
private final PersonRepository repository;
PersonHandler(PersonRepository repository) {
this.repository = repository;
}
Mono<ServerResponse> list(ServerRequest request) {
// Обратите внимание на пример другого порядка создания response от Flux
return ok().contentType(APPLICATION_JSON).body(repository.findAll(), Person.class);
}
}
}
@@ -0,0 +1,55 @@
package ru.otus.spring.domain;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
@Document
public class Person {
@Id
private String id;
@JsonProperty("name")
@Field("name")
private String lastName;
private int age;
public Person() {
}
public Person(String lastName) {
this.lastName = lastName;
}
public Person(String lastName, int age) {
this.lastName = lastName;
this.age = age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
@@ -0,0 +1,20 @@
package ru.otus.spring.repository;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import ru.otus.spring.domain.Person;
public interface PersonRepository
extends ReactiveMongoRepository<Person, String> {
Flux<Person> findAll();
Mono<Person> findById(String id);
Mono<Person> save(Mono<Person> person);
Flux<Person> findAllByLastName(String lastName);
Flux<Person> findAllByAge(int age);
}
@@ -0,0 +1,34 @@
package ru.otus.spring.rest;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
@RestController
public class AnnotatedController {
@GetMapping("/flux/one")
public Mono<String> one() {
return Mono.just("one")
.map(String::toUpperCase);
}
@GetMapping("/flux/ten")
public Flux<Integer> list() {
return Flux.range(1, 10);
}
@GetMapping(path = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream() {
return Flux.generate(() -> 0, (state, emitter) -> {
emitter.next(state);
return state + 1;
})
.delayElements(Duration.ofSeconds(1L))
.map(i -> "" + i);
}
}
@@ -0,0 +1,42 @@
package ru.otus.spring.rest;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import ru.otus.spring.domain.Person;
import ru.otus.spring.repository.PersonRepository;
@RestController
public class PersonController {
private PersonRepository repository;
public PersonController(PersonRepository repository) {
this.repository = repository;
}
@GetMapping("/person")
public Flux<Person> all() {
return repository.findAll();
}
@GetMapping("/person/{id}")
public Mono<Person> byId(@PathVariable("id") String id) {
return repository.findById(id);
}
@GetMapping("/person/byname")
public Flux<Person> byName(@RequestParam("name") String lastName) {
return repository.findAllByLastName(lastName);
}
@GetMapping("/person/byage")
public Flux<Person> byAge(@RequestParam int age) {
return repository.findAllByAge(age);
}
@PostMapping("/person")
public Mono<Person> save(@RequestBody Mono<Person> dto) {
return repository.save(dto);
}
}
@@ -0,0 +1,28 @@
package ru.otus.spring.repository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import ru.otus.spring.domain.Person;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@DataMongoTest
public class PersonRepositoryTest {
@Autowired
private PersonRepository repository;
@Test
public void shouldSetIdOnSave() {
Mono<Person> personMono = repository.save(new Person("Bill", 12));
StepVerifier
.create(personMono)
.assertNext(person -> assertNotNull(person.getId()))
.expectComplete()
.verify();
}
}
@@ -0,0 +1,28 @@
package ru.otus.spring.rest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
@SpringBootTest
public class PersonControllerTest {
@Autowired
private RouterFunction<ServerResponse> route;
@Test
public void testRoute() {
WebTestClient client = WebTestClient
.bindToRouterFunction(route)
.build();
client.get()
.uri("/func/person")
.exchange()
.expectStatus()
.isOk();
}
}
@@ -32,7 +32,7 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<version>1.18.20</version>
<optional>true</optional>
</dependency>
</dependencies>