add choreogr
This commit is contained in:
parent
fbcfeb072a
commit
09b4663b9a
@ -22,13 +22,20 @@
|
||||
*/
|
||||
package com.iluwatar.saga.choreography;
|
||||
|
||||
import com.iluwatar.saga.orchestration.ChapterResult;
|
||||
|
||||
/**
|
||||
* Chapter is an interface representing a contract for an external service.
|
||||
* @param <K> is type for passing params
|
||||
*/
|
||||
public interface Chapter<K> {
|
||||
* In that case, a service needs to make a decision what to do further
|
||||
* hence the server needs to get all context representing {@link Saga}
|
||||
* */
|
||||
public interface Chapter {
|
||||
|
||||
/**
|
||||
* In that case, every method is responsible to make a decision on what to do then
|
||||
* @param saga incoming saga
|
||||
* @return saga result
|
||||
*/
|
||||
Saga execute(Saga saga);
|
||||
|
||||
/**
|
||||
* @return service name.
|
||||
@ -37,17 +44,17 @@ public interface Chapter<K> {
|
||||
|
||||
/**
|
||||
* The operation executed in general case.
|
||||
* @param value incoming value
|
||||
* @return result {@link ChapterResult}
|
||||
* @param saga incoming saga
|
||||
* @return result {@link Saga}
|
||||
*/
|
||||
ChapterResult<K> process(K value);
|
||||
Saga process(Saga saga);
|
||||
|
||||
/**
|
||||
* The operation executed in rollback case.
|
||||
* @param value incoming value
|
||||
* @return result {@link ChapterResult}
|
||||
* @param saga incoming saga
|
||||
* @return result {@link Saga}
|
||||
*/
|
||||
ChapterResult<K> rollback(K value);
|
||||
Saga rollback(Saga saga);
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.saga.choreography;
|
||||
|
||||
|
||||
/**
|
||||
* Class representing a service to book a fly
|
||||
*/
|
||||
public class FlyBookingService extends Service {
|
||||
public FlyBookingService(ServiceDiscoveryService service) {
|
||||
super(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "booking a Fly";
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.saga.choreography;
|
||||
|
||||
|
||||
/**
|
||||
* Class representing a service to book a hotel
|
||||
*/
|
||||
public class HotelBookingService extends Service {
|
||||
public HotelBookingService(ServiceDiscoveryService service) {
|
||||
super(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "booking a Hotel";
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.saga.choreography;
|
||||
|
||||
|
||||
/**
|
||||
* Class representing a service to init a new order.
|
||||
*/
|
||||
public class OrderService extends Service{
|
||||
|
||||
public OrderService(ServiceDiscoveryService service) {
|
||||
super(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "init an order";
|
||||
}
|
||||
}
|
@ -22,6 +22,10 @@
|
||||
*/
|
||||
package com.iluwatar.saga.choreography;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Saga representation.
|
||||
* Saga consists of chapters.
|
||||
@ -29,6 +33,127 @@ package com.iluwatar.saga.choreography;
|
||||
*/
|
||||
public class Saga {
|
||||
|
||||
private List<Chapter> chapters;
|
||||
private int pos;
|
||||
private boolean forward;
|
||||
private boolean finished;
|
||||
|
||||
|
||||
public static Saga create() {
|
||||
return new Saga();
|
||||
}
|
||||
public SagaResult getResult() {
|
||||
return !finished ?
|
||||
SagaResult.PROGRESS :
|
||||
forward ?
|
||||
SagaResult.FINISHED :
|
||||
SagaResult.ROLLBACKED;
|
||||
}
|
||||
public Saga chapter(String name) {
|
||||
this.chapters.add(new Chapter(name));
|
||||
return this;
|
||||
}
|
||||
public Saga setInValue(Object value){
|
||||
if(chapters.isEmpty()){
|
||||
return this;
|
||||
}
|
||||
chapters.get(chapters.size()-1).setInValue(value);
|
||||
return this;
|
||||
}
|
||||
public Object getCurrentValue(){
|
||||
return chapters.get(pos).getInValue();
|
||||
}
|
||||
public void setCurrentValue(Object value){
|
||||
chapters.get(pos).setInValue(value);
|
||||
}
|
||||
public void setCurrentStatus(ChapterResult result){
|
||||
chapters.get(pos).setResult(result);
|
||||
}
|
||||
|
||||
void setFinished(boolean finished) {
|
||||
this.finished = finished;
|
||||
}
|
||||
boolean isForward() {
|
||||
return forward;
|
||||
}
|
||||
int forward() {
|
||||
return ++pos;
|
||||
}
|
||||
|
||||
int back() {
|
||||
this.forward = false;
|
||||
return --pos;
|
||||
}
|
||||
|
||||
|
||||
public Saga() {
|
||||
this.chapters = new ArrayList<>();
|
||||
this.pos = 0;
|
||||
this.forward = true;
|
||||
this.finished = false;
|
||||
}
|
||||
|
||||
Chapter getCurrent() {
|
||||
return chapters.get(pos);
|
||||
}
|
||||
|
||||
|
||||
boolean isPresent() {
|
||||
return pos >= 0 && pos < chapters.size();
|
||||
}
|
||||
boolean isCurrentSuccess(){
|
||||
return chapters.get(pos).isSuccess();
|
||||
}
|
||||
|
||||
/***
|
||||
* Class presents a chapter status and incoming parameters(incoming parameter transforms to outcoming parameter)
|
||||
*/
|
||||
public static class Chapter {
|
||||
private String name;
|
||||
private ChapterResult result;
|
||||
private Object inValue;
|
||||
|
||||
|
||||
public Chapter(String name) {
|
||||
this.name = name;
|
||||
this.result = ChapterResult.INIT;
|
||||
}
|
||||
|
||||
public Object getInValue() {
|
||||
return inValue;
|
||||
}
|
||||
|
||||
public void setInValue(Object object) {
|
||||
this.inValue = object;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setResult(ChapterResult result){
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public boolean isSuccess(){
|
||||
return result == ChapterResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum ChapterResult {
|
||||
INIT, SUCCESS, ROLLBACK
|
||||
}
|
||||
|
||||
public enum SagaResult {
|
||||
PROGRESS, FINISHED, ROLLBACKED
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Saga{" +
|
||||
"chapters=" + Arrays.toString(chapters.toArray()) +
|
||||
", pos=" + pos +
|
||||
", forward=" + forward +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.saga.choreography;
|
||||
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This pattern is used in distributed services to perform a group of operations atomically.
|
||||
* This is an analog of transaction in a database but in terms of microservices architecture this is executed
|
||||
* in a distributed environment
|
||||
* <p>
|
||||
* A saga is a sequence of local transactions in a certain context. If one transaction fails for some reason,
|
||||
* the saga executes compensating transactions(rollbacks) to undo the impact of the preceding transactions.
|
||||
* <p>
|
||||
* In this approach, there are no mediators or orchestrators services.
|
||||
* All chapters are handled and moved by services manually.
|
||||
* <p>
|
||||
* The major difference with choreography saga is an ability to handle crashed services
|
||||
* (otherwise in choreography services very hard to prevent a saga if one of them has been crashed)
|
||||
*
|
||||
* @see com.iluwatar.saga.choreography.Saga
|
||||
* @see Service
|
||||
*/
|
||||
public class SagaApplication {
|
||||
private static final Logger logger = LoggerFactory.getLogger(SagaApplication.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
ServiceDiscoveryService sd = serviceDiscovery();
|
||||
Chapter service = sd.findAny();
|
||||
Saga goodOrderSaga = service.execute(newSaga("good_order"));
|
||||
Saga badOrderSaga = service.execute(newSaga("bad_order"));
|
||||
logger.info("orders: goodOrder is {}, badOrder is {}",
|
||||
goodOrderSaga.getResult(), badOrderSaga.getResult());
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static Saga newSaga(Object value) {
|
||||
return Saga
|
||||
.create()
|
||||
.chapter("init an order").setInValue(value)
|
||||
.chapter("booking a Fly")
|
||||
.chapter("booking a Hotel")
|
||||
.chapter("withdrawing Money");
|
||||
}
|
||||
|
||||
private static ServiceDiscoveryService serviceDiscovery() {
|
||||
ServiceDiscoveryService sd = new ServiceDiscoveryService();
|
||||
return sd
|
||||
.discover(new OrderService(sd))
|
||||
.discover(new FlyBookingService(sd))
|
||||
.discover(new HotelBookingService(sd))
|
||||
.discover(new WithdrawMoneyService(sd));
|
||||
}
|
||||
}
|
103
saga/src/main/java/com/iluwatar/saga/choreography/Service.java
Normal file
103
saga/src/main/java/com/iluwatar/saga/choreography/Service.java
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.saga.choreography;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Common abstraction class representing services
|
||||
* implementing a general contract @see {@link Chapter}
|
||||
*/
|
||||
public abstract class Service implements Chapter {
|
||||
protected static final Logger logger = LoggerFactory.getLogger(Service.class);
|
||||
|
||||
private final ServiceDiscoveryService sd;
|
||||
|
||||
public Service(ServiceDiscoveryService service) {
|
||||
this.sd = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Saga execute(Saga saga) {
|
||||
Saga nextSaga = saga;
|
||||
Object nextVal;
|
||||
String chapterName = saga.getCurrent().getName();
|
||||
if (chapterName.equals(getName())) {
|
||||
if (saga.isForward()) {
|
||||
nextSaga = process(saga);
|
||||
nextVal = nextSaga.getCurrentValue();
|
||||
if (nextSaga.isCurrentSuccess()) {
|
||||
nextSaga.forward();
|
||||
} else {
|
||||
nextSaga.back();
|
||||
}
|
||||
} else {
|
||||
nextSaga = rollback(saga);
|
||||
nextVal = nextSaga.getCurrentValue();
|
||||
nextSaga.back();
|
||||
}
|
||||
|
||||
if (isSagaFinished(nextSaga)) {
|
||||
return nextSaga;
|
||||
}
|
||||
|
||||
nextSaga.setCurrentValue(nextVal);
|
||||
}
|
||||
Saga finalNextSaga = nextSaga;
|
||||
|
||||
return sd.find(chapterName).map(ch -> ch.execute(finalNextSaga))
|
||||
.orElseThrow(RuntimeException::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Saga process(Saga saga) {
|
||||
Object inValue = saga.getCurrentValue();
|
||||
logger.info("The chapter '{}' has been started. The data {} has been stored or calculated successfully",
|
||||
getName(), inValue);
|
||||
saga.setCurrentStatus(Saga.ChapterResult.SUCCESS);
|
||||
saga.setCurrentValue(inValue);
|
||||
return saga;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Saga rollback(Saga saga) {
|
||||
Object inValue = saga.getCurrentValue();
|
||||
logger.info("The Rollback for a chapter '{}' has been started. The data {} has been rollbacked successfully",
|
||||
getName(), inValue);
|
||||
|
||||
saga.setCurrentStatus(Saga.ChapterResult.ROLLBACK);
|
||||
saga.setCurrentValue(inValue);
|
||||
return saga;
|
||||
}
|
||||
|
||||
private boolean isSagaFinished(Saga saga) {
|
||||
if (!saga.isPresent()) {
|
||||
saga.setFinished(true);
|
||||
logger.info(" the saga has been finished with {} status", saga.getResult());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.saga.choreography;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* The class representing a service discovery pattern.
|
||||
*/
|
||||
public class ServiceDiscoveryService {
|
||||
private Map<String, Chapter> services;
|
||||
|
||||
/**
|
||||
* find any service
|
||||
* @return any service
|
||||
* @throws NoSuchElementException if no elements further
|
||||
*/
|
||||
public Chapter findAny(){
|
||||
return services.values().iterator().next();
|
||||
}
|
||||
|
||||
public Optional<Chapter> find(String service) {
|
||||
return Optional.ofNullable(services.getOrDefault(service, null));
|
||||
}
|
||||
|
||||
public ServiceDiscoveryService discover(Chapter chapterService) {
|
||||
services.put(chapterService.getName(), chapterService);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ServiceDiscoveryService() {
|
||||
this.services = new HashMap<>();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.saga.choreography;
|
||||
|
||||
|
||||
/**
|
||||
* Class representing a service to withdraw a money
|
||||
*/
|
||||
public class WithdrawMoneyService extends Service {
|
||||
|
||||
public WithdrawMoneyService(ServiceDiscoveryService service) {
|
||||
super(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "withdrawing Money";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Saga process(Saga saga) {
|
||||
Object inValue = saga.getCurrentValue();
|
||||
|
||||
if (inValue.equals("bad_order") ) {
|
||||
logger.info("The chapter '{}' has been started. But the exception has been raised." +
|
||||
"The rollback is about to start",
|
||||
getName(), inValue);
|
||||
saga.setCurrentStatus(Saga.ChapterResult.ROLLBACK);
|
||||
return saga;
|
||||
}
|
||||
return super.process(saga);
|
||||
}
|
||||
}
|
@ -28,7 +28,7 @@ import java.util.List;
|
||||
/**
|
||||
* Saga representation.
|
||||
* Saga consists of chapters.
|
||||
* Every Chapter is executed a certain service.
|
||||
* Every Chapter is executed by a certain service.
|
||||
*/
|
||||
public class Saga {
|
||||
|
||||
@ -39,7 +39,6 @@ public class Saga {
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Saga chapter(String name) {
|
||||
this.chapters.add(new Chapter(name));
|
||||
return this;
|
||||
|
@ -24,7 +24,6 @@
|
||||
package com.iluwatar.saga.orchestration;
|
||||
|
||||
|
||||
import com.iluwatar.saga.ServiceDiscoveryService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -38,6 +37,8 @@ import org.slf4j.LoggerFactory;
|
||||
*
|
||||
* In this approach, there is an orchestrator @see {@link SagaOrchestrator} that manages all the transactions and directs
|
||||
* the participant services to execute local transactions based on events.
|
||||
* The major difference with choreography saga is an ability to handle crashed services
|
||||
* (otherwise in choreography services very hard to prevent a saga if one of them has been crashed)
|
||||
*
|
||||
* @see Saga
|
||||
* @see SagaOrchestrator
|
||||
@ -49,7 +50,7 @@ public class SagaApplication {
|
||||
public static void main(String[] args) {
|
||||
SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery());
|
||||
|
||||
Saga.Result goodOrder = sagaOrchestrator.execute("god_order");
|
||||
Saga.Result goodOrder = sagaOrchestrator.execute("good_order");
|
||||
Saga.Result badOrder = sagaOrchestrator.execute("bad_order");
|
||||
Saga.Result crashedOrder = sagaOrchestrator.execute("crashed_order");
|
||||
|
||||
|
@ -22,7 +22,6 @@
|
||||
*/
|
||||
package com.iluwatar.saga.orchestration;
|
||||
|
||||
import com.iluwatar.saga.ServiceDiscoveryService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -48,6 +47,7 @@ public class SagaOrchestrator {
|
||||
}
|
||||
|
||||
/**
|
||||
* pipeline to execute saga process/story
|
||||
*
|
||||
* @param value incoming value
|
||||
* @param <K> type for incoming value
|
||||
|
@ -20,9 +20,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.saga;
|
||||
|
||||
import com.iluwatar.saga.orchestration.Chapter;
|
||||
package com.iluwatar.saga.orchestration;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.saga.choreography;
|
||||
|
||||
import com.iluwatar.saga.orchestration.SagaApplication;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class SagaApplicationTest {
|
||||
@Test
|
||||
public void mainTest() {
|
||||
SagaApplication.main(new String[]{});
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.saga.choreography;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SagaChoreographyTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void executeTest() {
|
||||
ServiceDiscoveryService sd = serviceDiscovery();
|
||||
Chapter service = sd.findAny();
|
||||
Saga badOrderSaga = service.execute(newSaga("bad_order"));
|
||||
Saga goodOrderSaga = service.execute(newSaga("good_order"));
|
||||
|
||||
Assert.assertEquals(badOrderSaga.getResult(), Saga.SagaResult.ROLLBACKED);
|
||||
Assert.assertEquals(goodOrderSaga.getResult(), Saga.SagaResult.FINISHED);
|
||||
}
|
||||
|
||||
private static Saga newSaga(Object value) {
|
||||
return Saga
|
||||
.create()
|
||||
.chapter("init an order").setInValue(value)
|
||||
.chapter("booking a Fly")
|
||||
.chapter("booking a Hotel")
|
||||
.chapter("withdrawing Money");
|
||||
}
|
||||
|
||||
private static ServiceDiscoveryService serviceDiscovery() {
|
||||
ServiceDiscoveryService sd = new ServiceDiscoveryService();
|
||||
return sd
|
||||
.discover(new OrderService(sd))
|
||||
.discover(new FlyBookingService(sd))
|
||||
.discover(new HotelBookingService(sd))
|
||||
.discover(new WithdrawMoneyService(sd));
|
||||
}
|
||||
}
|
@ -1,3 +1,25 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.saga.orchestration;
|
||||
|
||||
import org.junit.Test;
|
||||
|
@ -1,6 +1,27 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.saga.orchestration;
|
||||
|
||||
import com.iluwatar.saga.ServiceDiscoveryService;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
@ -12,7 +33,7 @@ public class SagaOrchestratorInternallyTest {
|
||||
private List<String> records = new ArrayList<>();
|
||||
|
||||
@Test
|
||||
public void execute() {
|
||||
public void executeTest() {
|
||||
SagaOrchestrator sagaOrchestrator = new SagaOrchestrator(newSaga(), serviceDiscovery());
|
||||
Saga.Result result = sagaOrchestrator.execute(1);
|
||||
Assert.assertEquals(result, Saga.Result.ROLLBACK);
|
||||
|
@ -1,6 +1,27 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.saga.orchestration;
|
||||
|
||||
import com.iluwatar.saga.ServiceDiscoveryService;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user