Introduction
This documentation contains some help to examples from spring-examples repository is contains some spring / spring-boot playground projects
1. spring-boot under the hood
1.1. build and run
bash gradlew -t bootRun
1.2. spring-boot POWER
// check if bean is in spring factory:
@OnBeanCondition
// check if class is in classpath:
@OnClassCondition
// on thruthy evaluated express condition:
@OnExpressionCondition
// on expected java version
@OnJavaCondition
// on expected JNDI branch
@OnJndiCondition
// check if property exists
@OnPropertyCondition
// check if resource exists
@OnResourceCondition
// check if WebApplicationContext exists
@OnWebApplicationCondition
// AND condition
@AllNestedConditions
// OR condition
@AnyNestedConditions
// NOT condition
@NoneNestedConditions
1.3. own starters
-
create a starter configuration with provided by you custom functionality. In our case it’s kind of Java analog for JSON.stringify / JSON.parse from JS.
-
create src/main/resources/META-INF/spring.factories file
TODO: finish readme…
1.4. links
2. server-side events / sse emitter
2.1. test
open 2 browser windows and start chatting
curl localhost:8080/subscriptions # terminal 1
http --timeout 2629746000 --stream :8080/subscriptions # terminal 2
http post :8080/subscriptions/broadcast message="trololo" # send message to all subscribers
http delete :8080/subscriptions/2017-09-22-01-18-22-693 # unsubscribe by id
2.2. links
2.3. TODO
3. retry
3.1. test
bash gradlew clean build bootRun
http :8080 09:32:02
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Date: Fri, 22 Sep 2017 06:32:04 GMT
Transfer-Encoding: chunked
{
"message": {
"attempts": 2,
"createdAt": "2017-09-22T06:32:04.074+0000"
}
}
http :8080 09:32:04
HTTP/1.1 400
Connection: close
Content-Type: application/json;charset=UTF-8
Date: Fri, 22 Sep 2017 06:32:06 GMT
Transfer-Encoding: chunked
{
"error": "fuck..."
}
links:
4. frontend web resources optimization
We can optimize resources in many different way. Here are 3:
-
using Gradle plugin for NodeJS with common frontend npm flow
-
using Wro4j Gradle Plugin
-
using Gradle JS/CSS Plugins
4.1. test
bash gradlew build
bash spring-boot-webpack/build/libs/spring-boot-webpack-0.0.1.jar --server.port=8000
bash spring-boot-wro4j/build/libs/spring-boot-wro4j-0.0.1.jar --server.port=8000
bash gradlew build
bash gradlew s-b-webpack:bRun
bash gradlew s-b-webpack:ui:yarn_watch
bash gradlew s-b-wro:bRun
bash gradles s-b-wro:processWebResources -t
bash gradlew s-b-g-j-c-p:bRun
bash gradles s-b-g-j-c-p:webResources -t
http :8080
links:
5. testing
TODO: In progress…
bash gradlew clean test
5.1. mokito
@RunWith(MockitoJUnitRunner.class) /* 1 */
public class MokitoTest {
@Mock
RandomService randomService; /* 2 */
@InjectMocks
UnstableResource sut; /* 3 */
@Test
public void testMock() throws Exception {
val res = sut.unstable(); /* 4 */
assertThat(res, notNullValue());
}
}
bash gradlew clean m:test
6. CQRS and Event-Sourcing
6.1. test
bash gradlew bootRun
http :8080/api/v1/order/items
[
{
"createdAt": "2017-09-30 09:08:23.160 +0000",
"id": "cddca9831a0",
"localDateTime": "2017-09-30 12:08:23.160 Z",
"name": "two",
"price": 4.82
},
{
"createdAt": "2017-09-30 09:08:23.044 +0000",
"id": "ef37433ecb6",
"localDateTime": "2017-09-30 12:08:23.046 Z",
"name": "one",
"price": 4.21
},
{
"createdAt": "2017-09-30 09:08:23.275 +0000",
"id": "a6ad2f9fa48",
"localDateTime": "2017-09-30 12:08:23.275 Z",
"name": "three",
"price": 6.4
}
]
curl localhost:8080/api/v1/add-to-card -d'{"itemIds":["a6ad2f9fa48"]}' -H'content-type:application/json'
a6d3c929e35
## logs:
# 2017-09-30 12:09:00.952 : create: CreateOrderEvent(transactionId=a6d3c929e35, itemIds=[a6ad2f9fa48], createdAt=2017-09-30T12:09:00.950)
# 2017-09-30 12:09:00.955 : add: AddToCardEvent(transactionId=a6d3c929e35, itemId=a6ad2f9fa48, createdAt=2017-09-30T12:09:00.954)
# 2017-09-30 12:09:00.956 : store AddToCardEvent(transactionId=a6d3c929e35, itemId=a6ad2f9fa48, createdAt=2017-09-30T12:09:00.954)
# 2017-09-30 12:09:00.956 : store CreateOrderEvent(transactionId=a6d3c929e35, itemIds=[a6ad2f9fa48], createdAt=2017-09-30T12:09:00.950)
http post :8080/api/v1/add-to-card/a6d3c929e35 itemIds:='["ef37433ecb6"]'
a6d3c929e35
## logs:
# 2017-09-30 12:10:28.083 : create: CreateOrderEvent(transactionId=a6d3c929e35, itemIds=[ef37433ecb6], createdAt=2017-09-30T12:10:28.083)
# 2017-09-30 12:10:28.083 : add: AddToCardEvent(transactionId=a6d3c929e35, itemId=ef37433ecb6, createdAt=2017-09-30T12:10:28.083)
# 2017-09-30 12:10:28.083 : store AddToCardEvent(transactionId=a6d3c929e35, itemId=ef37433ecb6, createdAt=2017-09-30T12:10:28.083)
# 2017-09-30 12:10:28.083 : store CreateOrderEvent(transactionId=a6d3c929e35, itemIds=[ef37433ecb6], createdAt=2017-09-30T12:10:28.083)
curl localhost:8080/api/v1/order/a6d3c929e35 | jq
{
"id": "a6d3c929e35",
"itemIds": [
"a6ad2f9fa48",
"ef37433ecb6"
],
"done": false,
"createdAt": "2017-09-30 09:09:00.955 +0000",
"localDateTime": "2017-09-30 12:09:00.955 Z"
}
http post :8080/api/v1/order/a6d3c929e35
## logs:
# 2017-09-30 12:11:20.081 : ship: ShipOrderEvent(transactionId=a6d3c929e35, createdAt=2017-09-30T12:11:20.081)
# 2017-09-30 12:11:20.083 : publish: ShopItem(id=a6ad2f9fa48, name=three, price=6.40, createdAt=2017-09-30T12:08:23.275+03:00[Europe/Kiev], localDateTime=2017-09-30T12:08:23.275)
# 2017-09-30 12:11:20.083 : publish: ShopItem(id=ef37433ecb6, name=one, price=4.21, createdAt=2017-09-30T12:08:23.044+03:00[Europe/Kiev], localDateTime=2017-09-30T12:08:23.046)
# 2017-09-30 12:11:20.083 : publish: ShipOrderEvent(transactionId=a6d3c929e35, createdAt=2017-09-30T12:11:20.081)
# 2017-09-30 12:11:20.083 : delivery: DeliveryOrderEvent(transactionId=a6d3c929e35, createdAt=2017-09-30T12:11:20.083)
# 2017-09-30 12:11:20.084 : store DeliveryOrderEvent(transactionId=a6d3c929e35, createdAt=2017-09-30T12:11:20.083)
# 2017-09-30 12:11:20.084 : store ShipOrderEvent(transactionId=a6d3c929e35, createdAt=2017-09-30T12:11:20.081)
curl localhost:8080/api/v1/order/a6d3c929e35 | jq
{
"id": "a6d3c929e35",
"itemIds": [
"a6ad2f9fa48",
"ef37433ecb6"
],
"done": true,
"createdAt": "2017-09-30 09:09:00.955 +0000",
"localDateTime": "2017-09-30 12:09:00.955 Z"
}
http :8080/api/v1/order/items
[
{
"createdAt": "2017-09-30 09:08:23.160 +0000",
"id": "cddca9831a0",
"localDateTime": "2017-09-30 12:08:23.160 Z",
"name": "two",
"price": 4.82
}
]
http :8080/api/v1/events
[
{
"createdAt": "2017093012090095043740950000000",
"itemIds": [
"a6ad2f9fa48"
],
"transactionId": "a6d3c929e35",
"type": "CreateOrderEvent"
},
{
"createdAt": "2017093012090095443740954000000",
"itemId": "a6ad2f9fa48",
"transactionId": "a6d3c929e35",
"type": "AddToCardEvent"
},
{
"createdAt": "2017093012102808343828083000000",
"itemId": "ef37433ecb6",
"transactionId": "a6d3c929e35",
"type": "AddToCardEvent"
},
{
"createdAt": "2017093012102808343828083000000",
"itemIds": [
"ef37433ecb6"
],
"transactionId": "a6d3c929e35",
"type": "CreateOrderEvent"
},
{
"createdAt": "2017093012112008143880081000000",
"transactionId": "a6d3c929e35",
"type": "ShipOrderEvent"
},
{
"createdAt": "2017093012112008343880083000000",
"transactionId": "a6d3c929e35",
"type": "DeliveryOrderEvent"
}
]
6.2. links
Spring Cloud Stream | CQRS and Event Sourcing
bash gradlew clean build
bash gradlew bootRun --parallel
open http://localhost:8001
Flow still buggy, but i’m too lazy to fix…
http post :8080/
[
{
"href": "http://localhost:8080/47401f30-b3f1-4204-9ea4-bdb8c394d0b3",
"rel": "self"
},
{
"href": "http://localhost:8080/47401f30-b3f1-4204-9ea4-bdb8c394d0b3/activate",
"rel": "activate"
}
]
http http://localhost:8080/47401f30-b3f1-4204-9ea4-bdb8c394d0b3
{
"activated": false,
"changes": [
{
"id": "47401f30-b3f1-4204-9ea4-bdb8c394d0b3"
}
],
"deactivated": false,
"id": "47401f30-b3f1-4204-9ea4-bdb8c394d0b3",
"nickname": "anonymous",
"state": "INITIALIZED"
}
http post http://localhost:8080/47401f30-b3f1-4204-9ea4-bdb8c394d0b3/activate
[
{
"href": "http://localhost:8080/47401f30-b3f1-4204-9ea4-bdb8c394d0b3",
"rel": "self"
},
{
"href": "http://localhost:8080/47401f30-b3f1-4204-9ea4-bdb8c394d0b3/change-nickname/newNickname",
"rel": "change-nickname"
}
]
http post http://localhost:8080/47401f30-b3f1-4204-9ea4-bdb8c394d0b3/change-nickname/max
[
{
"href": "http://localhost:8080/47401f30-b3f1-4204-9ea4-bdb8c394d0b3",
"rel": "self"
},
{
"href": "http://localhost:8080/47401f30-b3f1-4204-9ea4-bdb8c394d0b3/deactivate",
"rel": "deactivate"
}
]
http post http://localhost:8080/47401f30-b3f1-4204-9ea4-bdb8c394d0b3/deactivate [ { "href": "http://localhost:8080/47401f30-b3f1-4204-9ea4-bdb8c394d0b3", "rel": "self" }, { "href": "http://localhost:8080/47401f30-b3f1-4204-9ea4-bdb8c394d0b3/replay", "rel": "replay" } ]
http http://localhost:8080/47401f30-b3f1-4204-9ea4-bdb8c394d0b3/replay [ { "UserInitializedEvent": { "id": "47401f30-b3f1-4204-9ea4-bdb8c394d0b3" } }, { "UserActivatedEvent": {} }, { "UserActivatedEvent": {} }, { "NicknameChangedEvent": { "nickname": "max" } }, { "UserActivatedEvent": {} }, { "UserActivatedEvent": {} }, { "NicknameChangedEvent": { "nickname": "max" } }, { "UserDeactivatedEvent": {} } ]
resources:
7. Axon Framework
Axon banking app
7.1. old
bash gradlew bootRun
open http://localhost:8001 # browser 1
open http://localhost:8001 # browser 2
7.2. links
Axon complaints app
bash gradlew bootRun
http :8080
http post :8080 company=first description="oh, no!"
http :8080/uuid...
7.3. links
./mvnw clean package spring-boot:run http post :8080/api/v1/orders # output: # Location: http://localhost:8080/api/v1/orders/$ID http put :8080/api/v1/orders/$ID/add k1=2 k3=4 http put :8080/api/v1/orders/$ID/add k1=1 k3=1 http put :8080/api/v1/orders/$ID/remove k1=1 k3=2 http delete :8080/api/v1/orders/$ID http post :8080/api/v1/orders http post :8080/api/v1/orders http :8080/order-query
FIXME: Failed with snapshots threshold…
-
MongoDB + Axon Framework App
-
Maven / Gradle Kotlin configuration
./gradlew java -jar axon-app/build/libs/*.jar java -jar reactive-client/build/libs/*.jar --server.port=8000 java -jar es-client/build/libs/*.jar --server.port=8888 # create main entrance http :8080/api/v1/entrance/register entranceId=main # stream event client #http --stream --timeout=123456 :8000 curl localhost:8000 # unlock main entrance http put http://localhost:8080/api/v1/entrance/main/unlock # create and unlock reception entrance http :8080/api/v1/entrance/register entranceId=reception http put http://localhost:8080/api/v1/entrance/reception/unlock http :8080/api/v1/guest/register name=max # ... Location: http://localhost:8080/api/v1/guest/646fa336-dda6-4fdd-be38-05179ecd44e7/activate http put http://localhost:8080/api/v1/guest/646fa336-dda6-4fdd-be38-05179ecd44e7/activate # enter inside building / door http post http://localhost:8080/api/v1/entrance/main/enter/646fa336-dda6-4fdd-be38-05179ecd44e7 http post http://localhost:8080/api/v1/entrance/reception/enter/646fa336-dda6-4fdd-be38-05179ecd44e7 http post http://localhost:8080/api/v1/entrance/reception/exit/646fa336-dda6-4fdd-be38-05179ecd44e7 http post http://localhost:8080/api/v1/entrance/main/exit/646fa336-dda6-4fdd-be38-05179ecd44e7 http :8888 http :8888 accept:application/json http delete http://localhost:8080/api/v1/entrance/reception http delete http://localhost:8080/api/v1/entrance/main
./gradlew build composeUp ./gradlew composeDown
Axon / Spring-boot using Kotlin
./mvnw java -jar target/*.jar http post :8080/api/v1/counter\? counterId=c http put :8080/api/v1/counter/c/enable http put :8080/api/v1/counter/c/increment http put :8080/api/v1/counter/c/decrement\?amount=2 http :8080 http :8080\?collection=events
./gradlew build composeUp ./gradlew composeDown
In fucking progress..
-
axon
-
spring-boot 1.x
-
mongodb
-
kotlin
-
gradle
-
maven
./mvnw # or java -jar target/*.jar ./gradlew java -jar build/libs/*.jar http :8080/api/room http :8080/api/room roomId=my-room http :8080/api/room/my-room http put :8080/api/room/my-room/max http put :8080/api/room/my-room/valery http :8080/api/member http delete :8080/api/room/my-room/max http delete :8080/api/room/my-room/valery http :8080\?collection=events
./gradlew build composeUp ./gradlew composeDown
links:
In fucking progress..
./mvnw java -jar target/*.jar # voting begin http :8080/api/v1/registration id=my-vote name=my=vote # or http :8080/api/v1/registration name=my=vote # 201: Location: http://localhost:8080/api/v1/registration/approve/my-vote # ... { "PUT": "http://localhost:8080/api/v1/registration/approve/my-vote", "message": "candidate has been registered. Please approve registration." } http put http://localhost:8080/api/v1/registration/approve/my-vote # 202: { "message": "Registration has been approved. Wait for elections begin and send your campaign URL to your electorate making vote for you!", "vote by electorId in request body - POST": "http://localhost:8080/api/v1/vote/my-vote" } http post http://localhost:8080/api/v1/vote/my-vote electorId=dag http post http://localhost:8080/api/v1/vote/my-vote electorId=max # 202, and logs: handling VotedForCandidateEvent(candidateId=my-vote, elector={"electorId": "max"}) http :8080 # output - current snapshot state... # voting end # counter begin http post :8080/api/v1/counter\?counterId=my-counter # or http post :8080/api/v1/counter http put :8080//api/v1/counter/my-counter/enable http post :8080//api/v1/counter/my-counter/increment http post :8080//api/v1/counter/my-counter/increment http :8080 # counter end # show all events http :8080\?collection=events # show all snapshots http :8080 # same as http :8080\?collection=snapshots
8. Jersey JAX-RS
bash gradlew bootRun
# rest
http :8080
http :8080/1
# global error handling
http :8080/4
# actuator
http :8080/application
http :8080/application/status
bash gradlew bootRun
# secured actuatoe endpoints
http --auth user:change-me :8080/applicaion/env
http --auth admin:change-me :8080/applicaion/env
# secured rest
http --auth user:change-me :8080/1
http --auth admin:change-me :8080/2
# check logs for
# ... user was here
# ... admin was here