Resolves checkstyle errors for guarded-suspension, half-sync-half-async, hexagonal (#1064)

* Reduces checkstyle errors in guarded-suspension

* Reduces checkstyle errors in half-sync-half-async

* Reduces checkstyle errors in hexagonal
This commit is contained in:
Anurag Agarwal 2019-11-10 22:31:32 +05:30 committed by Ilkka Seppälä
parent 4f9ee0189c
commit dda09535e6
34 changed files with 382 additions and 359 deletions

View File

@ -21,15 +21,6 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
/**
* Guarded-suspension is a concurrent design pattern for handling situation when to execute some action we need
* condition to be satisfied.
* <p>
* Implementation is based on GuardedQueue, which has two methods: get and put,
* the condition is that we cannot get from empty queue so when thread attempt
* to break the condition we invoke Object's wait method on him and when other thread put an element
* to the queue he notify the waiting one that now he can get from queue.
*/
package com.iluwatar.guarded.suspension; package com.iluwatar.guarded.suspension;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@ -38,10 +29,18 @@ import java.util.concurrent.TimeUnit;
/** /**
* Created by robertt240 on 1/26/17. * Created by robertt240 on 1/26/17.
*
* <p>Guarded-suspension is a concurrent design pattern for handling situation when to execute some
* action we need condition to be satisfied.
*
* <p>Implementation is based on GuardedQueue, which has two methods: get and put, the condition is
* that we cannot get from empty queue so when thread attempt to break the condition we invoke
* Object's wait method on him and when other thread put an element to the queue he notify the
* waiting one that now he can get from queue.
*/ */
public class App { public class App {
/** /**
* Example pattern execution * Example pattern execution.
* *
* @param args - command line args * @param args - command line args
*/ */
@ -55,13 +54,15 @@ public class App {
} }
); );
//here we wait two seconds to show that the thread which is trying to get from guardedQueue will be waiting // here we wait two seconds to show that the thread which is trying
// to get from guardedQueue will be waiting
try { try {
Thread.sleep(2000); Thread.sleep(2000);
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} }
//now we execute second thread which will put number to guardedQueue and notify first thread that it could get // now we execute second thread which will put number to guardedQueue
// and notify first thread that it could get
executorService.execute(() -> { executorService.execute(() -> {
guardedQueue.put(20); guardedQueue.put(20);
} }

View File

@ -23,16 +23,16 @@
package com.iluwatar.guarded.suspension; package com.iluwatar.guarded.suspension;
import java.util.LinkedList;
import java.util.Queue;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.LinkedList;
import java.util.Queue;
/** /**
* Guarded Queue is an implementation for Guarded Suspension Pattern * Guarded Queue is an implementation for Guarded Suspension Pattern Guarded suspension pattern is
* Guarded suspension pattern is used to handle a situation when you want to execute a method * used to handle a situation when you want to execute a method on an object which is not in a
* on an object which is not in a proper state. * proper state.
*
* @see <a href="http://java-design-patterns.com/patterns/guarded-suspension/">http://java-design-patterns.com/patterns/guarded-suspension/</a> * @see <a href="http://java-design-patterns.com/patterns/guarded-suspension/">http://java-design-patterns.com/patterns/guarded-suspension/</a>
*/ */
public class GuardedQueue { public class GuardedQueue {
@ -44,6 +44,8 @@ public class GuardedQueue {
} }
/** /**
* Get the last element of the queue is exists.
*
* @return last element of a queue if queue is not empty * @return last element of a queue if queue is not empty
*/ */
public synchronized Integer get() { public synchronized Integer get() {
@ -60,6 +62,8 @@ public class GuardedQueue {
} }
/** /**
* Put a value in the queue.
*
* @param e number which we want to put to our queue * @param e number which we want to put to our queue
*/ */
public synchronized void put(Integer e) { public synchronized void put(Integer e) {

View File

@ -23,42 +23,35 @@
package com.iluwatar.halfsynchalfasync; package com.iluwatar.halfsynchalfasync;
import java.util.concurrent.LinkedBlockingQueue;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
/** /**
* This application demonstrates Half-Sync/Half-Async pattern. Key parts of the pattern are {@link
* AsyncTask} and {@link AsynchronousService}.
* *
* This application demonstrates Half-Sync/Half-Async pattern. Key parts of the pattern are * <p><i>PROBLEM</i> <br>
* {@link AsyncTask} and {@link AsynchronousService}.
*
* <p>
* <i>PROBLEM</i> <br>
* A concurrent system have a mixture of short duration, mid duration and long duration tasks. Mid * A concurrent system have a mixture of short duration, mid duration and long duration tasks. Mid
* or long duration tasks should be performed asynchronously to meet quality of service * or long duration tasks should be performed asynchronously to meet quality of service
* requirements. * requirements.
* *
* <p> * <p><i>INTENT</i> <br>
* <i>INTENT</i> <br>
* The intent of this pattern is to separate the the synchronous and asynchronous processing in the * The intent of this pattern is to separate the the synchronous and asynchronous processing in the
* concurrent application by introducing two intercommunicating layers - one for sync and one for * concurrent application by introducing two intercommunicating layers - one for sync and one for
* async. This simplifies the programming without unduly affecting the performance. * async. This simplifies the programming without unduly affecting the performance.
* *
* <p> * <p><i>APPLICABILITY</i> <br>
* <i>APPLICABILITY</i> <br> * UNIX network subsystems - In operating systems network operations are carried out asynchronously
* UNIX network subsystems - In operating systems network operations are carried out * with help of hardware level interrupts.<br> CORBA - At the asynchronous layer one thread is
* asynchronously with help of hardware level interrupts.<br> * associated with each socket that is connected to the client. Thread blocks waiting for CORBA
* CORBA - At the asynchronous layer one thread is associated with each socket that is connected * requests from the client. On receiving request it is inserted in the queuing layer which is then
* to the client. Thread blocks waiting for CORBA requests from the client. On receiving request it * picked up by synchronous layer which processes the request and sends response back to the
* is inserted in the queuing layer which is then picked up by synchronous layer which processes the * client.<br> Android AsyncTask framework - Framework provides a way to execute long running
* request and sends response back to the client.<br> * blocking calls, such as downloading a file, in background threads so that the UI thread remains
* Android AsyncTask framework - Framework provides a way to execute long running blocking * free to respond to user inputs.<br>
* calls, such as downloading a file, in background threads so that the UI thread remains free to
* respond to user inputs.<br>
* *
* <p> * <p><i>IMPLEMENTATION</i> <br>
* <i>IMPLEMENTATION</i> <br>
* The main method creates an asynchronous service which does not block the main thread while the * The main method creates an asynchronous service which does not block the main thread while the
* task is being performed. The main thread continues its work which is similar to Async Method * task is being performed. The main thread continues its work which is similar to Async Method
* Invocation pattern. The difference between them is that there is a queuing layer between * Invocation pattern. The difference between them is that there is a queuing layer between
@ -66,14 +59,13 @@ import java.util.concurrent.LinkedBlockingQueue;
* between both layers. Such as Priority Queue can be used as queuing layer to prioritize the way * between both layers. Such as Priority Queue can be used as queuing layer to prioritize the way
* tasks are executed. Our implementation is just one simple way of implementing this pattern, there * tasks are executed. Our implementation is just one simple way of implementing this pattern, there
* are many variants possible as described in its applications. * are many variants possible as described in its applications.
*
*/ */
public class App { public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class); private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/** /**
* Program entry point * Program entry point.
* *
* @param args command line args * @param args command line args
*/ */
@ -100,15 +92,13 @@ public class App {
} }
/** /**
* * ArithmeticSumTask.
* ArithmeticSumTask
*
*/ */
static class ArithmeticSumTask implements AsyncTask<Long> { static class ArithmeticSumTask implements AsyncTask<Long> {
private long n; private long numberOfElements;
public ArithmeticSumTask(long n) { public ArithmeticSumTask(long numberOfElements) {
this.n = n; this.numberOfElements = numberOfElements;
} }
/* /*
@ -117,7 +107,7 @@ public class App {
*/ */
@Override @Override
public Long call() throws Exception { public Long call() throws Exception {
return ap(n); return ap(numberOfElements);
} }
/* /*
@ -128,7 +118,7 @@ public class App {
*/ */
@Override @Override
public void onPreCall() { public void onPreCall() {
if (n < 0) { if (numberOfElements < 0) {
throw new IllegalArgumentException("n is less than 0"); throw new IllegalArgumentException("n is less than 0");
} }
} }

View File

@ -23,15 +23,14 @@
package com.iluwatar.halfsynchalfasync; package com.iluwatar.halfsynchalfasync;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.FutureTask; import java.util.concurrent.FutureTask;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* This is the asynchronous layer which does not block when a new request arrives. It just passes * This is the asynchronous layer which does not block when a new request arrives. It just passes
@ -63,13 +62,14 @@ public class AsynchronousService {
/** /**
* A non-blocking method which performs the task provided in background and returns immediately. * A non-blocking method which performs the task provided in background and returns immediately.
* <p> *
* On successful completion of task the result is posted back using callback method * <p>On successful completion of task the result is posted back using callback method {@link
* {@link AsyncTask#onPostCall(Object)}, if task execution is unable to complete normally due to * AsyncTask#onPostCall(Object)}, if task execution is unable to complete normally due to some
* some exception then the reason for error is posted back using callback method * exception then the reason for error is posted back using callback method {@link
* {@link AsyncTask#onError(Throwable)}. * AsyncTask#onError(Throwable)}.
* <p> *
* NOTE: The results are posted back in the context of background thread in this implementation. * <p>NOTE: The results are posted back in the context of background thread in this
* implementation.
*/ */
public <T> void execute(final AsyncTask<T> task) { public <T> void execute(final AsyncTask<T> task) {
try { try {

View File

@ -31,40 +31,35 @@ import com.iluwatar.hexagonal.module.LotteryTestingModule;
import com.iluwatar.hexagonal.sampledata.SampleData; import com.iluwatar.hexagonal.sampledata.SampleData;
/** /**
* Hexagonal Architecture pattern decouples the application core from the services it uses. This
* allows the services to be plugged in and the application will run with or without the services.
* *
* Hexagonal Architecture pattern decouples the application core from the * <p>The core logic, or business logic, of an application consists of the algorithms that are
* services it uses. This allows the services to be plugged in and the * essential to its purpose. They implement the use cases that are the heart of the application.
* application will run with or without the services.<p> * When you change them, you change the essence of the application.
* *
* The core logic, or business logic, of an application consists of the * <p>The services are not essential. They can be replaced without changing the purpose of the
* algorithms that are essential to its purpose. They implement the use * application. Examples: database access and other types of storage, user interface components,
* cases that are the heart of the application. When you change them, you * e-mail and other communication components, hardware devices.
* change the essence of the application.<p>
* *
* The services are not essential. They can be replaced without changing * <p>This example demonstrates Hexagonal Architecture with a lottery system. The application core
* the purpose of the application. Examples: database access and other * is separate from the services that drive it and from the services it uses.
* types of storage, user interface components, e-mail and other
* communication components, hardware devices.<p>
* *
* This example demonstrates Hexagonal Architecture with a lottery system. * <p>The primary ports for the application are console interfaces {@link
* The application core is separate from the services that drive it and * com.iluwatar.hexagonal.administration.ConsoleAdministration} through which the lottery round is
* from the services it uses.<p> * initiated and run and {@link com.iluwatar.hexagonal.service.ConsoleLottery} that allows players
* * to submit lottery tickets for the draw.
* The primary ports for the application are console interfaces
* {@link com.iluwatar.hexagonal.administration.ConsoleAdministration} through which the lottery round is
* initiated and run and {@link com.iluwatar.hexagonal.service.ConsoleLottery} that allows players to
* submit lottery tickets for the draw.<p>
*
* The secondary ports that application core uses are {@link com.iluwatar.hexagonal.banking.WireTransfers}
* which is a banking service, {@link com.iluwatar.hexagonal.eventlog.LotteryEventLog} that delivers
* eventlog as lottery events occur and {@link com.iluwatar.hexagonal.database.LotteryTicketRepository}
* that is the storage for the lottery tickets.
* *
* <p>The secondary ports that application core uses are{@link
* com.iluwatar.hexagonal.banking.WireTransfers} which is a banking service, {@link
* com.iluwatar.hexagonal.eventlog.LotteryEventLog} that delivers eventlog as lottery events occur
* and {@link com.iluwatar.hexagonal.database.LotteryTicketRepository} that is the storage for the
* lottery tickets.
*/ */
public class App { public class App {
/** /**
* Program entry point * Program entry point.
*/ */
public static void main(String[] args) { public static void main(String[] args) {

View File

@ -30,20 +30,19 @@ import com.iluwatar.hexagonal.domain.LotteryService;
import com.iluwatar.hexagonal.module.LotteryModule; import com.iluwatar.hexagonal.module.LotteryModule;
import com.iluwatar.hexagonal.mongo.MongoConnectionPropertiesLoader; import com.iluwatar.hexagonal.mongo.MongoConnectionPropertiesLoader;
import com.iluwatar.hexagonal.sampledata.SampleData; import com.iluwatar.hexagonal.sampledata.SampleData;
import java.util.Scanner;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Scanner;
/** /**
* Console interface for lottery administration * Console interface for lottery administration.
*/ */
public class ConsoleAdministration { public class ConsoleAdministration {
private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleAdministration.class); private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleAdministration.class);
/** /**
* Program entry point * Program entry point.
*/ */
public static void main(String[] args) { public static void main(String[] args) {
MongoConnectionPropertiesLoader.load(); MongoConnectionPropertiesLoader.load();
@ -51,7 +50,8 @@ public class ConsoleAdministration {
LotteryAdministration administration = injector.getInstance(LotteryAdministration.class); LotteryAdministration administration = injector.getInstance(LotteryAdministration.class);
LotteryService service = injector.getInstance(LotteryService.class); LotteryService service = injector.getInstance(LotteryService.class);
SampleData.submitTickets(service, 20); SampleData.submitTickets(service, 20);
ConsoleAdministrationSrv consoleAdministration = new ConsoleAdministrationSrvImpl(administration, LOGGER); ConsoleAdministrationSrv consoleAdministration =
new ConsoleAdministrationSrvImpl(administration, LOGGER);
try (Scanner scanner = new Scanner(System.in)) { try (Scanner scanner = new Scanner(System.in)) {
boolean exit = false; boolean exit = false;
while (!exit) { while (!exit) {

View File

@ -24,22 +24,22 @@
package com.iluwatar.hexagonal.administration; package com.iluwatar.hexagonal.administration;
/** /**
* Console interface for lottery administration * Console interface for lottery administration.
*/ */
public interface ConsoleAdministrationSrv { public interface ConsoleAdministrationSrv {
/** /**
* Get all submitted tickets * Get all submitted tickets.
*/ */
void getAllSubmittedTickets(); void getAllSubmittedTickets();
/** /**
* Draw lottery numbers * Draw lottery numbers.
*/ */
void performLottery(); void performLottery();
/** /**
* Begin new lottery round * Begin new lottery round.
*/ */
void resetLottery(); void resetLottery();
} }

View File

@ -28,14 +28,14 @@ import com.iluwatar.hexagonal.domain.LotteryNumbers;
import org.slf4j.Logger; import org.slf4j.Logger;
/** /**
* Console implementation for lottery administration * Console implementation for lottery administration.
*/ */
public class ConsoleAdministrationSrvImpl implements ConsoleAdministrationSrv { public class ConsoleAdministrationSrvImpl implements ConsoleAdministrationSrv {
private final LotteryAdministration administration; private final LotteryAdministration administration;
private final Logger logger; private final Logger logger;
/** /**
* Constructor * Constructor.
*/ */
public ConsoleAdministrationSrvImpl(LotteryAdministration administration, Logger logger) { public ConsoleAdministrationSrvImpl(LotteryAdministration administration, Logger logger) {
this.administration = administration; this.administration = administration;
@ -44,7 +44,8 @@ public class ConsoleAdministrationSrvImpl implements ConsoleAdministrationSrv {
@Override @Override
public void getAllSubmittedTickets() { public void getAllSubmittedTickets() {
administration.getAllSubmittedTickets().forEach((k, v) -> logger.info("Key: {}, Value: {}", k, v)); administration.getAllSubmittedTickets()
.forEach((k, v) -> logger.info("Key: {}, Value: {}", k, v));
} }
@Override @Override

View File

@ -23,22 +23,20 @@
package com.iluwatar.hexagonal.banking; package com.iluwatar.hexagonal.banking;
import com.iluwatar.hexagonal.domain.LotteryConstants;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.iluwatar.hexagonal.domain.LotteryConstants;
/** /**
* * Banking implementation.
* Banking implementation
*
*/ */
public class InMemoryBank implements WireTransfers { public class InMemoryBank implements WireTransfers {
private static Map<String, Integer> accounts = new HashMap<>(); private static Map<String, Integer> accounts = new HashMap<>();
static { static {
accounts.put(LotteryConstants.SERVICE_BANK_ACCOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT_BALANCE); accounts
.put(LotteryConstants.SERVICE_BANK_ACCOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT_BALANCE);
} }
@Override @Override
@ -52,10 +50,10 @@ public class InMemoryBank implements WireTransfers {
} }
@Override @Override
public boolean transferFunds(int amount, String sourceBackAccount, String destinationBankAccount) { public boolean transferFunds(int amount, String sourceAccount, String destinationAccount) {
if (accounts.getOrDefault(sourceBackAccount, 0) >= amount) { if (accounts.getOrDefault(sourceAccount, 0) >= amount) {
accounts.put(sourceBackAccount, accounts.get(sourceBackAccount) - amount); accounts.put(sourceAccount, accounts.get(sourceAccount) - amount);
accounts.put(destinationBankAccount, accounts.get(destinationBankAccount) + amount); accounts.put(destinationAccount, accounts.get(destinationAccount) + amount);
return true; return true;
} else { } else {
return false; return false;

View File

@ -27,13 +27,12 @@ import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase; import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.UpdateOptions; import com.mongodb.client.model.UpdateOptions;
import org.bson.Document;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.bson.Document;
/** /**
* Mongo based banking adapter * Mongo based banking adapter.
*/ */
public class MongoBank implements WireTransfers { public class MongoBank implements WireTransfers {
@ -45,28 +44,28 @@ public class MongoBank implements WireTransfers {
private MongoCollection<Document> accountsCollection; private MongoCollection<Document> accountsCollection;
/** /**
* Constructor * Constructor.
*/ */
public MongoBank() { public MongoBank() {
connect(); connect();
} }
/** /**
* Constructor accepting parameters * Constructor accepting parameters.
*/ */
public MongoBank(String dbName, String accountsCollectionName) { public MongoBank(String dbName, String accountsCollectionName) {
connect(dbName, accountsCollectionName); connect(dbName, accountsCollectionName);
} }
/** /**
* Connect to database with default parameters * Connect to database with default parameters.
*/ */
public void connect() { public void connect() {
connect(DEFAULT_DB, DEFAULT_ACCOUNTS_COLLECTION); connect(DEFAULT_DB, DEFAULT_ACCOUNTS_COLLECTION);
} }
/** /**
* Connect to database with given parameters * Connect to database with given parameters.
*/ */
public void connect(String dbName, String accountsCollectionName) { public void connect(String dbName, String accountsCollectionName) {
if (mongoClient != null) { if (mongoClient != null) {
@ -79,6 +78,8 @@ public class MongoBank implements WireTransfers {
} }
/** /**
* Get mongo client.
*
* @return mongo client * @return mongo client
*/ */
public MongoClient getMongoClient() { public MongoClient getMongoClient() {
@ -86,6 +87,7 @@ public class MongoBank implements WireTransfers {
} }
/** /**
* Get mongo database.
* *
* @return mongo database * @return mongo database
*/ */
@ -94,6 +96,7 @@ public class MongoBank implements WireTransfers {
} }
/** /**
* Get accounts collection.
* *
* @return accounts collection * @return accounts collection
*/ */
@ -106,7 +109,8 @@ public class MongoBank implements WireTransfers {
public void setFunds(String bankAccount, int amount) { public void setFunds(String bankAccount, int amount) {
Document search = new Document("_id", bankAccount); Document search = new Document("_id", bankAccount);
Document update = new Document("_id", bankAccount).append("funds", amount); Document update = new Document("_id", bankAccount).append("funds", amount);
accountsCollection.updateOne(search, new Document("$set", update), new UpdateOptions().upsert(true)); accountsCollection
.updateOne(search, new Document("$set", update), new UpdateOptions().upsert(true));
} }
@Override @Override
@ -121,14 +125,14 @@ public class MongoBank implements WireTransfers {
} }
@Override @Override
public boolean transferFunds(int amount, String sourceBackAccount, String destinationBankAccount) { public boolean transferFunds(int amount, String sourceAccount, String destinationAccount) {
int sourceFunds = getFunds(sourceBackAccount); int sourceFunds = getFunds(sourceAccount);
if (sourceFunds < amount) { if (sourceFunds < amount) {
return false; return false;
} else { } else {
int destFunds = getFunds(destinationBankAccount); int destFunds = getFunds(destinationAccount);
setFunds(sourceBackAccount, sourceFunds - amount); setFunds(sourceAccount, sourceFunds - amount);
setFunds(destinationBankAccount, destFunds + amount); setFunds(destinationAccount, destFunds + amount);
return true; return true;
} }
} }

View File

@ -24,24 +24,22 @@
package com.iluwatar.hexagonal.banking; package com.iluwatar.hexagonal.banking;
/** /**
*
* Interface to bank accounts. * Interface to bank accounts.
*
*/ */
public interface WireTransfers { public interface WireTransfers {
/** /**
* Set amount of funds for bank account * Set amount of funds for bank account.
*/ */
void setFunds(String bankAccount, int amount); void setFunds(String bankAccount, int amount);
/** /**
* Get amount of funds for bank account * Get amount of funds for bank account.
*/ */
int getFunds(String bankAccount); int getFunds(String bankAccount);
/** /**
* Transfer funds from one bank account to another * Transfer funds from one bank account to another.
*/ */
boolean transferFunds(int amount, String sourceBackAccount, String destinationBankAccount); boolean transferFunds(int amount, String sourceBackAccount, String destinationBankAccount);

View File

@ -23,17 +23,14 @@
package com.iluwatar.hexagonal.database; package com.iluwatar.hexagonal.database;
import com.iluwatar.hexagonal.domain.LotteryTicket;
import com.iluwatar.hexagonal.domain.LotteryTicketId;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import com.iluwatar.hexagonal.domain.LotteryTicket;
import com.iluwatar.hexagonal.domain.LotteryTicketId;
/** /**
*
* Mock database for lottery tickets. * Mock database for lottery tickets.
*
*/ */
public class InMemoryTicketRepository implements LotteryTicketRepository { public class InMemoryTicketRepository implements LotteryTicketRepository {

View File

@ -23,36 +23,33 @@
package com.iluwatar.hexagonal.database; package com.iluwatar.hexagonal.database;
import com.iluwatar.hexagonal.domain.LotteryTicket;
import com.iluwatar.hexagonal.domain.LotteryTicketId;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import com.iluwatar.hexagonal.domain.LotteryTicket;
import com.iluwatar.hexagonal.domain.LotteryTicketId;
/** /**
*
* Interface for accessing lottery tickets in database. * Interface for accessing lottery tickets in database.
*
*/ */
public interface LotteryTicketRepository { public interface LotteryTicketRepository {
/** /**
* Find lottery ticket by id * Find lottery ticket by id.
*/ */
Optional<LotteryTicket> findById(LotteryTicketId id); Optional<LotteryTicket> findById(LotteryTicketId id);
/** /**
* Save lottery ticket * Save lottery ticket.
*/ */
Optional<LotteryTicketId> save(LotteryTicket ticket); Optional<LotteryTicketId> save(LotteryTicket ticket);
/** /**
* Get all lottery tickets * Get all lottery tickets.
*/ */
Map<LotteryTicketId, LotteryTicket> findAll(); Map<LotteryTicketId, LotteryTicket> findAll();
/** /**
* Delete all lottery tickets * Delete all lottery tickets.
*/ */
void deleteAll(); void deleteAll();

View File

@ -30,8 +30,6 @@ import com.iluwatar.hexagonal.domain.PlayerDetails;
import com.mongodb.MongoClient; import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase; import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -40,9 +38,10 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.bson.Document;
/** /**
* Mongo lottery ticket database * Mongo lottery ticket database.
*/ */
public class MongoTicketRepository implements LotteryTicketRepository { public class MongoTicketRepository implements LotteryTicketRepository {
@ -56,14 +55,14 @@ public class MongoTicketRepository implements LotteryTicketRepository {
private MongoCollection<Document> countersCollection; private MongoCollection<Document> countersCollection;
/** /**
* Constructor * Constructor.
*/ */
public MongoTicketRepository() { public MongoTicketRepository() {
connect(); connect();
} }
/** /**
* Constructor accepting parameters * Constructor accepting parameters.
*/ */
public MongoTicketRepository(String dbName, String ticketsCollectionName, public MongoTicketRepository(String dbName, String ticketsCollectionName,
String countersCollectionName) { String countersCollectionName) {
@ -71,14 +70,14 @@ public class MongoTicketRepository implements LotteryTicketRepository {
} }
/** /**
* Connect to database with default parameters * Connect to database with default parameters.
*/ */
public void connect() { public void connect() {
connect(DEFAULT_DB, DEFAULT_TICKETS_COLLECTION, DEFAULT_COUNTERS_COLLECTION); connect(DEFAULT_DB, DEFAULT_TICKETS_COLLECTION, DEFAULT_COUNTERS_COLLECTION);
} }
/** /**
* Connect to database with given parameters * Connect to database with given parameters.
*/ */
public void connect(String dbName, String ticketsCollectionName, public void connect(String dbName, String ticketsCollectionName,
String countersCollectionName) { String countersCollectionName) {
@ -101,6 +100,8 @@ public class MongoTicketRepository implements LotteryTicketRepository {
} }
/** /**
* Get next ticket id.
*
* @return next ticket id * @return next ticket id
*/ */
public int getNextId() { public int getNextId() {
@ -112,6 +113,7 @@ public class MongoTicketRepository implements LotteryTicketRepository {
} }
/** /**
* Get tickets collection.
* *
* @return tickets collection * @return tickets collection
*/ */
@ -120,6 +122,7 @@ public class MongoTicketRepository implements LotteryTicketRepository {
} }
/** /**
* Get counters collection.
* *
* @return counters collection * @return counters collection
*/ */
@ -155,7 +158,7 @@ public class MongoTicketRepository implements LotteryTicketRepository {
public Map<LotteryTicketId, LotteryTicket> findAll() { public Map<LotteryTicketId, LotteryTicket> findAll() {
Map<LotteryTicketId, LotteryTicket> map = new HashMap<>(); Map<LotteryTicketId, LotteryTicket> map = new HashMap<>();
List<Document> docs = ticketsCollection.find(new Document()).into(new ArrayList<>()); List<Document> docs = ticketsCollection.find(new Document()).into(new ArrayList<>());
for (Document doc: docs) { for (Document doc : docs) {
LotteryTicket lotteryTicket = docToTicket(doc); LotteryTicket lotteryTicket = docToTicket(doc);
map.put(lotteryTicket.getId(), lotteryTicket); map.put(lotteryTicket.getId(), lotteryTicket);
} }
@ -174,6 +177,7 @@ public class MongoTicketRepository implements LotteryTicketRepository {
.map(Integer::parseInt) .map(Integer::parseInt)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
LotteryNumbers lotteryNumbers = LotteryNumbers.create(numbers); LotteryNumbers lotteryNumbers = LotteryNumbers.create(numbers);
return new LotteryTicket(new LotteryTicketId(doc.getInteger("ticketId")), playerDetails, lotteryNumbers); return new LotteryTicket(new LotteryTicketId(doc
.getInteger("ticketId")), playerDetails, lotteryNumbers);
} }
} }

View File

@ -27,13 +27,10 @@ import com.google.inject.Inject;
import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.banking.WireTransfers;
import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepository;
import com.iluwatar.hexagonal.eventlog.LotteryEventLog; import com.iluwatar.hexagonal.eventlog.LotteryEventLog;
import java.util.Map; import java.util.Map;
/** /**
* * Lottery administration implementation.
* Lottery administration implementation
*
*/ */
public class LotteryAdministration { public class LotteryAdministration {
@ -42,7 +39,7 @@ public class LotteryAdministration {
private final WireTransfers wireTransfers; private final WireTransfers wireTransfers;
/** /**
* Constructor * Constructor.
*/ */
@Inject @Inject
public LotteryAdministration(LotteryTicketRepository repository, LotteryEventLog notifications, public LotteryAdministration(LotteryTicketRepository repository, LotteryEventLog notifications,
@ -53,14 +50,14 @@ public class LotteryAdministration {
} }
/** /**
* Get all the lottery tickets submitted for lottery * Get all the lottery tickets submitted for lottery.
*/ */
public Map<LotteryTicketId, LotteryTicket> getAllSubmittedTickets() { public Map<LotteryTicketId, LotteryTicket> getAllSubmittedTickets() {
return repository.findAll(); return repository.findAll();
} }
/** /**
* Draw lottery numbers * Draw lottery numbers.
*/ */
public LotteryNumbers performLottery() { public LotteryNumbers performLottery() {
LotteryNumbers numbers = LotteryNumbers.createRandom(); LotteryNumbers numbers = LotteryNumbers.createRandom();
@ -69,11 +66,14 @@ public class LotteryAdministration {
LotteryTicketCheckResult result = LotteryUtils.checkTicketForPrize(repository, id, numbers); LotteryTicketCheckResult result = LotteryUtils.checkTicketForPrize(repository, id, numbers);
if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) {
boolean transferred = wireTransfers.transferFunds(LotteryConstants.PRIZE_AMOUNT, boolean transferred = wireTransfers.transferFunds(LotteryConstants.PRIZE_AMOUNT,
LotteryConstants.SERVICE_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount()); LotteryConstants.SERVICE_BANK_ACCOUNT, tickets.get(id).getPlayerDetails()
.getBankAccount());
if (transferred) { if (transferred) {
notifications.ticketWon(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); notifications
.ticketWon(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT);
} else { } else {
notifications.prizeError(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); notifications
.prizeError(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT);
} }
} else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) { } else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) {
notifications.ticketDidNotWin(tickets.get(id).getPlayerDetails()); notifications.ticketDidNotWin(tickets.get(id).getPlayerDetails());
@ -83,7 +83,7 @@ public class LotteryAdministration {
} }
/** /**
* Begin new lottery round * Begin new lottery round.
*/ */
public void resetLottery() { public void resetLottery() {
repository.deleteAll(); repository.deleteAll();

View File

@ -24,9 +24,7 @@
package com.iluwatar.hexagonal.domain; package com.iluwatar.hexagonal.domain;
/** /**
* * Lottery domain constants.
* Lottery domain constants
*
*/ */
public class LotteryConstants { public class LotteryConstants {

View File

@ -24,7 +24,6 @@
package com.iluwatar.hexagonal.domain; package com.iluwatar.hexagonal.domain;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.PrimitiveIterator; import java.util.PrimitiveIterator;
@ -32,10 +31,8 @@ import java.util.Random;
import java.util.Set; import java.util.Set;
/** /**
* * Value object representing lottery numbers. This lottery uses sets of 4 numbers. The numbers must
* Value object representing lottery numbers. This lottery uses sets of 4 numbers. The numbers must be unique and * be unique and between 1 and 20.
* between 1 and 20.
*
*/ */
public class LotteryNumbers { public class LotteryNumbers {
@ -62,6 +59,8 @@ public class LotteryNumbers {
} }
/** /**
* Creates a random lottery number.
*
* @return random LotteryNumbers * @return random LotteryNumbers
*/ */
public static LotteryNumbers createRandom() { public static LotteryNumbers createRandom() {
@ -69,6 +68,8 @@ public class LotteryNumbers {
} }
/** /**
* Creates lottery number from given set of numbers.
*
* @return given LotteryNumbers * @return given LotteryNumbers
*/ */
public static LotteryNumbers create(Set<Integer> givenNumbers) { public static LotteryNumbers create(Set<Integer> givenNumbers) {
@ -76,6 +77,8 @@ public class LotteryNumbers {
} }
/** /**
* Get numbers.
*
* @return lottery numbers * @return lottery numbers
*/ */
public Set<Integer> getNumbers() { public Set<Integer> getNumbers() {
@ -83,6 +86,8 @@ public class LotteryNumbers {
} }
/** /**
* Get numbers as string.
*
* @return numbers as comma separated string * @return numbers as comma separated string
*/ */
public String getNumbersAsString() { public String getNumbersAsString() {
@ -107,16 +112,15 @@ public class LotteryNumbers {
} }
/** /**
*
* Helper class for generating random numbers. * Helper class for generating random numbers.
*
*/ */
private static class RandomNumberGenerator { private static class RandomNumberGenerator {
private PrimitiveIterator.OfInt randomIterator; private PrimitiveIterator.OfInt randomIterator;
/** /**
* Initialize a new random number generator that generates random numbers in the range [min, max] * Initialize a new random number generator that generates random numbers in the range [min,
* max].
* *
* @param min the min value (inclusive) * @param min the min value (inclusive)
* @param max the max value (inclusive) * @param max the max value (inclusive)
@ -126,6 +130,8 @@ public class LotteryNumbers {
} }
/** /**
* Gets next random integer in [min, max] range.
*
* @return a random number in the range (min, max) * @return a random number in the range (min, max)
*/ */
public int nextInt() { public int nextInt() {

View File

@ -27,13 +27,10 @@ import com.google.inject.Inject;
import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.banking.WireTransfers;
import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepository;
import com.iluwatar.hexagonal.eventlog.LotteryEventLog; import com.iluwatar.hexagonal.eventlog.LotteryEventLog;
import java.util.Optional; import java.util.Optional;
/** /**
* * Implementation for lottery service.
* Implementation for lottery service
*
*/ */
public class LotteryService { public class LotteryService {
@ -42,7 +39,7 @@ public class LotteryService {
private final WireTransfers wireTransfers; private final WireTransfers wireTransfers;
/** /**
* Constructor * Constructor.
*/ */
@Inject @Inject
public LotteryService(LotteryTicketRepository repository, LotteryEventLog notifications, public LotteryService(LotteryTicketRepository repository, LotteryEventLog notifications,
@ -53,7 +50,7 @@ public class LotteryService {
} }
/** /**
* Submit lottery ticket to participate in the lottery * Submit lottery ticket to participate in the lottery.
*/ */
public Optional<LotteryTicketId> submitTicket(LotteryTicket ticket) { public Optional<LotteryTicketId> submitTicket(LotteryTicket ticket) {
boolean result = wireTransfers.transferFunds(LotteryConstants.TICKET_PRIZE, boolean result = wireTransfers.transferFunds(LotteryConstants.TICKET_PRIZE,
@ -70,9 +67,12 @@ public class LotteryService {
} }
/** /**
* Check if lottery ticket has won * Check if lottery ticket has won.
*/ */
public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { public LotteryTicketCheckResult checkTicketForPrize(
LotteryTicketId id,
LotteryNumbers winningNumbers
) {
return LotteryUtils.checkTicketForPrize(repository, id, winningNumbers); return LotteryUtils.checkTicketForPrize(repository, id, winningNumbers);
} }
} }

View File

@ -24,9 +24,7 @@
package com.iluwatar.hexagonal.domain; package com.iluwatar.hexagonal.domain;
/** /**
*
* Immutable value object representing lottery ticket. * Immutable value object representing lottery ticket.
*
*/ */
public class LotteryTicket { public class LotteryTicket {
@ -44,6 +42,8 @@ public class LotteryTicket {
} }
/** /**
* Get player details.
*
* @return player details * @return player details
*/ */
public PlayerDetails getPlayerDetails() { public PlayerDetails getPlayerDetails() {
@ -51,6 +51,8 @@ public class LotteryTicket {
} }
/** /**
* Get lottery numbers.
*
* @return lottery numbers * @return lottery numbers
*/ */
public LotteryNumbers getNumbers() { public LotteryNumbers getNumbers() {
@ -58,6 +60,8 @@ public class LotteryTicket {
} }
/** /**
* Get ticket id.
*
* @return id * @return id
*/ */
public LotteryTicketId getId() { public LotteryTicketId getId() {
@ -65,7 +69,7 @@ public class LotteryTicket {
} }
/** /**
* set id * Set ticket id.
*/ */
public void setId(LotteryTicketId id) { public void setId(LotteryTicketId id) {
this.id = id; this.id = id;

View File

@ -24,16 +24,18 @@
package com.iluwatar.hexagonal.domain; package com.iluwatar.hexagonal.domain;
/** /**
*
* Represents lottery ticket check result. * Represents lottery ticket check result.
*
*/ */
public class LotteryTicketCheckResult { public class LotteryTicketCheckResult {
/** /**
* Enumeration of Type of Outcomes of a Lottery * Enumeration of Type of Outcomes of a Lottery.
*/ */
public enum CheckResult { WIN_PRIZE, NO_PRIZE, TICKET_NOT_SUBMITTED } public enum CheckResult {
WIN_PRIZE,
NO_PRIZE,
TICKET_NOT_SUBMITTED
}
private final CheckResult checkResult; private final CheckResult checkResult;
private final int prizeAmount; private final int prizeAmount;
@ -55,6 +57,8 @@ public class LotteryTicketCheckResult {
} }
/** /**
* Get result.
*
* @return check result * @return check result
*/ */
public CheckResult getResult() { public CheckResult getResult() {
@ -62,6 +66,8 @@ public class LotteryTicketCheckResult {
} }
/** /**
* Get prize amount.
*
* @return prize amount * @return prize amount
*/ */
public int getPrizeAmount() { public int getPrizeAmount() {

View File

@ -26,7 +26,7 @@ package com.iluwatar.hexagonal.domain;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
/** /**
* Lottery ticked id * Lottery ticked id.
*/ */
public class LotteryTicketId { public class LotteryTicketId {

View File

@ -24,11 +24,11 @@
package com.iluwatar.hexagonal.domain; package com.iluwatar.hexagonal.domain;
import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepository;
import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult;
import java.util.Optional; import java.util.Optional;
/** /**
* Lottery utilities * Lottery utilities.
*/ */
public class LotteryUtils { public class LotteryUtils {
@ -36,19 +36,22 @@ public class LotteryUtils {
} }
/** /**
* Checks if lottery ticket has won * Checks if lottery ticket has won.
*/ */
public static LotteryTicketCheckResult checkTicketForPrize(LotteryTicketRepository repository, LotteryTicketId id, public static LotteryTicketCheckResult checkTicketForPrize(
LotteryNumbers winningNumbers) { LotteryTicketRepository repository,
LotteryTicketId id,
LotteryNumbers winningNumbers
) {
Optional<LotteryTicket> optional = repository.findById(id); Optional<LotteryTicket> optional = repository.findById(id);
if (optional.isPresent()) { if (optional.isPresent()) {
if (optional.get().getNumbers().equals(winningNumbers)) { if (optional.get().getNumbers().equals(winningNumbers)) {
return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.WIN_PRIZE, 1000); return new LotteryTicketCheckResult(CheckResult.WIN_PRIZE, 1000);
} else { } else {
return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.NO_PRIZE); return new LotteryTicketCheckResult(CheckResult.NO_PRIZE);
} }
} else { } else {
return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.TICKET_NOT_SUBMITTED); return new LotteryTicketCheckResult(CheckResult.TICKET_NOT_SUBMITTED);
} }
} }
} }

View File

@ -24,9 +24,7 @@
package com.iluwatar.hexagonal.domain; package com.iluwatar.hexagonal.domain;
/** /**
*
* Immutable value object containing lottery player details. * Immutable value object containing lottery player details.
*
*/ */
public class PlayerDetails { public class PlayerDetails {
@ -44,6 +42,8 @@ public class PlayerDetails {
} }
/** /**
* Get email.
*
* @return email * @return email
*/ */
public String getEmail() { public String getEmail() {
@ -51,6 +51,8 @@ public class PlayerDetails {
} }
/** /**
* Get back account number.
*
* @return bank account number * @return bank account number
*/ */
public String getBankAccount() { public String getBankAccount() {
@ -58,6 +60,8 @@ public class PlayerDetails {
} }
/** /**
* Get phone number.
*
* @return phone number * @return phone number
*/ */
public String getPhoneNumber() { public String getPhoneNumber() {

View File

@ -26,34 +26,32 @@ package com.iluwatar.hexagonal.eventlog;
import com.iluwatar.hexagonal.domain.PlayerDetails; import com.iluwatar.hexagonal.domain.PlayerDetails;
/** /**
* * Event log for lottery events.
* Event log for lottery events
*
*/ */
public interface LotteryEventLog { public interface LotteryEventLog {
/** /**
* lottery ticket submitted * lottery ticket submitted.
*/ */
void ticketSubmitted(PlayerDetails details); void ticketSubmitted(PlayerDetails details);
/** /**
* error submitting lottery ticket * error submitting lottery ticket.
*/ */
void ticketSubmitError(PlayerDetails details); void ticketSubmitError(PlayerDetails details);
/** /**
* lottery ticket did not win * lottery ticket did not win.
*/ */
void ticketDidNotWin(PlayerDetails details); void ticketDidNotWin(PlayerDetails details);
/** /**
* lottery ticket won * lottery ticket won.
*/ */
void ticketWon(PlayerDetails details, int prizeAmount); void ticketWon(PlayerDetails details, int prizeAmount);
/** /**
* error paying the prize * error paying the prize.
*/ */
void prizeError(PlayerDetails details, int prizeAmount); void prizeError(PlayerDetails details, int prizeAmount);

View File

@ -30,7 +30,7 @@ import com.mongodb.client.MongoDatabase;
import org.bson.Document; import org.bson.Document;
/** /**
* Mongo based event log * Mongo based event log.
*/ */
public class MongoEventLog implements LotteryEventLog { public class MongoEventLog implements LotteryEventLog {
@ -44,28 +44,28 @@ public class MongoEventLog implements LotteryEventLog {
private StdOutEventLog stdOutEventLog = new StdOutEventLog(); private StdOutEventLog stdOutEventLog = new StdOutEventLog();
/** /**
* Constructor * Constructor.
*/ */
public MongoEventLog() { public MongoEventLog() {
connect(); connect();
} }
/** /**
* Constructor accepting parameters * Constructor accepting parameters.
*/ */
public MongoEventLog(String dbName, String eventsCollectionName) { public MongoEventLog(String dbName, String eventsCollectionName) {
connect(dbName, eventsCollectionName); connect(dbName, eventsCollectionName);
} }
/** /**
* Connect to database with default parameters * Connect to database with default parameters.
*/ */
public void connect() { public void connect() {
connect(DEFAULT_DB, DEFAULT_EVENTS_COLLECTION); connect(DEFAULT_DB, DEFAULT_EVENTS_COLLECTION);
} }
/** /**
* Connect to database with given parameters * Connect to database with given parameters.
*/ */
public void connect(String dbName, String eventsCollectionName) { public void connect(String dbName, String eventsCollectionName) {
if (mongoClient != null) { if (mongoClient != null) {
@ -78,6 +78,8 @@ public class MongoEventLog implements LotteryEventLog {
} }
/** /**
* Get mongo client.
*
* @return mongo client * @return mongo client
*/ */
public MongoClient getMongoClient() { public MongoClient getMongoClient() {
@ -85,6 +87,7 @@ public class MongoEventLog implements LotteryEventLog {
} }
/** /**
* Get mongo database.
* *
* @return mongo database * @return mongo database
*/ */
@ -93,8 +96,9 @@ public class MongoEventLog implements LotteryEventLog {
} }
/** /**
* Get events collection.
* *
* @return accounts collection * @return events collection
*/ */
public MongoCollection<Document> getEventsCollection() { public MongoCollection<Document> getEventsCollection() {
return eventsCollection; return eventsCollection;
@ -106,7 +110,8 @@ public class MongoEventLog implements LotteryEventLog {
Document document = new Document("email", details.getEmail()); Document document = new Document("email", details.getEmail());
document.put("phone", details.getPhoneNumber()); document.put("phone", details.getPhoneNumber());
document.put("bank", details.getBankAccount()); document.put("bank", details.getBankAccount());
document.put("message", "Lottery ticket was submitted and bank account was charged for 3 credits."); document
.put("message", "Lottery ticket was submitted and bank account was charged for 3 credits.");
eventsCollection.insertOne(document); eventsCollection.insertOne(document);
stdOutEventLog.ticketSubmitted(details); stdOutEventLog.ticketSubmitted(details);
} }
@ -136,7 +141,8 @@ public class MongoEventLog implements LotteryEventLog {
Document document = new Document("email", details.getEmail()); Document document = new Document("email", details.getEmail());
document.put("phone", details.getPhoneNumber()); document.put("phone", details.getPhoneNumber());
document.put("bank", details.getBankAccount()); document.put("bank", details.getBankAccount());
document.put("message", String.format("Lottery ticket won! The bank account was deposited with %d credits.", document.put("message", String
.format("Lottery ticket won! The bank account was deposited with %d credits.",
prizeAmount)); prizeAmount));
eventsCollection.insertOne(document); eventsCollection.insertOne(document);
stdOutEventLog.ticketWon(details, prizeAmount); stdOutEventLog.ticketWon(details, prizeAmount);
@ -147,7 +153,8 @@ public class MongoEventLog implements LotteryEventLog {
Document document = new Document("email", details.getEmail()); Document document = new Document("email", details.getEmail());
document.put("phone", details.getPhoneNumber()); document.put("phone", details.getPhoneNumber());
document.put("bank", details.getBankAccount()); document.put("bank", details.getBankAccount());
document.put("message", String.format("Lottery ticket won! Unfortunately the bank credit transfer of %d failed.", document.put("message", String
.format("Lottery ticket won! Unfortunately the bank credit transfer of %d failed.",
prizeAmount)); prizeAmount));
eventsCollection.insertOne(document); eventsCollection.insertOne(document);
stdOutEventLog.prizeError(details, prizeAmount); stdOutEventLog.prizeError(details, prizeAmount);

View File

@ -28,7 +28,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* Standard output event log * Standard output event log.
*/ */
public class StdOutEventLog implements LotteryEventLog { public class StdOutEventLog implements LotteryEventLog {
@ -42,7 +42,9 @@ public class StdOutEventLog implements LotteryEventLog {
@Override @Override
public void ticketDidNotWin(PlayerDetails details) { public void ticketDidNotWin(PlayerDetails details) {
LOGGER.info("Lottery ticket for {} was checked and unfortunately did not win this time.", details.getEmail()); LOGGER
.info("Lottery ticket for {} was checked and unfortunately did not win this time.", details
.getEmail());
} }
@Override @Override
@ -53,13 +55,14 @@ public class StdOutEventLog implements LotteryEventLog {
@Override @Override
public void prizeError(PlayerDetails details, int prizeAmount) { public void prizeError(PlayerDetails details, int prizeAmount) {
LOGGER.error("Lottery ticket for {} has won! Unfortunately the bank credit transfer of {} failed.", LOGGER
details.getEmail(), prizeAmount); .error("Lottery ticket for {} has won! Unfortunately the bank credit transfer of"
+ " {} failed.", details.getEmail(), prizeAmount);
} }
@Override @Override
public void ticketSubmitError(PlayerDetails details) { public void ticketSubmitError(PlayerDetails details) {
LOGGER.error("Lottery ticket for {} could not be submitted because the credit transfer of 3 credits failed.", LOGGER.error("Lottery ticket for {} could not be submitted because the credit transfer"
details.getEmail()); + " of 3 credits failed.", details.getEmail());
} }
} }

View File

@ -32,7 +32,7 @@ import com.iluwatar.hexagonal.eventlog.LotteryEventLog;
import com.iluwatar.hexagonal.eventlog.MongoEventLog; import com.iluwatar.hexagonal.eventlog.MongoEventLog;
/** /**
* Guice module for binding production dependencies * Guice module for binding production dependencies.
*/ */
public class LotteryModule extends AbstractModule { public class LotteryModule extends AbstractModule {
@Override @Override

View File

@ -32,7 +32,7 @@ import com.iluwatar.hexagonal.eventlog.LotteryEventLog;
import com.iluwatar.hexagonal.eventlog.StdOutEventLog; import com.iluwatar.hexagonal.eventlog.StdOutEventLog;
/** /**
* Guice module for testing dependencies * Guice module for testing dependencies.
*/ */
public class LotteryTestingModule extends AbstractModule { public class LotteryTestingModule extends AbstractModule {
@Override @Override

View File

@ -27,7 +27,7 @@ import java.io.FileInputStream;
import java.util.Properties; import java.util.Properties;
/** /**
* Mongo connection properties loader * Mongo connection properties loader.
*/ */
public class MongoConnectionPropertiesLoader { public class MongoConnectionPropertiesLoader {
@ -35,8 +35,7 @@ public class MongoConnectionPropertiesLoader {
private static final int DEFAULT_PORT = 27017; private static final int DEFAULT_PORT = 27017;
/** /**
* Try to load connection properties from file. * Try to load connection properties from file. Fall back to default connection properties.
* Fall back to default connection properties.
*/ */
public static void load() { public static void load() {
String host = DEFAULT_HOST; String host = DEFAULT_HOST;

View File

@ -30,12 +30,11 @@ import com.iluwatar.hexagonal.domain.LotteryService;
import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicket;
import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.LotteryTicketId;
import com.iluwatar.hexagonal.domain.PlayerDetails; import com.iluwatar.hexagonal.domain.PlayerDetails;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
/** /**
* Utilities for creating sample lottery tickets * Utilities for creating sample lottery tickets.
*/ */
public class SampleData { public class SampleData {
@ -92,7 +91,7 @@ public class SampleData {
} }
/** /**
* Inserts lottery tickets into the database based on the sample data * Inserts lottery tickets into the database based on the sample data.
*/ */
public static void submitTickets(LotteryService lotteryService, int numTickets) { public static void submitTickets(LotteryService lotteryService, int numTickets) {
for (int i = 0; i < numTickets; i++) { for (int i = 0; i < numTickets; i++) {

View File

@ -29,25 +29,24 @@ import com.iluwatar.hexagonal.banking.WireTransfers;
import com.iluwatar.hexagonal.domain.LotteryService; import com.iluwatar.hexagonal.domain.LotteryService;
import com.iluwatar.hexagonal.module.LotteryModule; import com.iluwatar.hexagonal.module.LotteryModule;
import com.iluwatar.hexagonal.mongo.MongoConnectionPropertiesLoader; import com.iluwatar.hexagonal.mongo.MongoConnectionPropertiesLoader;
import java.util.Scanner;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Scanner;
/** /**
* Console interface for lottery players * Console interface for lottery players.
*/ */
public class ConsoleLottery { public class ConsoleLottery {
private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleLottery.class); private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleLottery.class);
/** /**
* Program entry point * Program entry point.
*/ */
public static void main(String[] args) { public static void main(String[] args) {
MongoConnectionPropertiesLoader.load(); MongoConnectionPropertiesLoader.load();
Injector injector = Guice.createInjector(new LotteryModule()); Injector injector = Guice.createInjector(new LotteryModule());
LotteryService service = injector.getInstance( LotteryService.class); LotteryService service = injector.getInstance(LotteryService.class);
WireTransfers bank = injector.getInstance(WireTransfers.class); WireTransfers bank = injector.getInstance(WireTransfers.class);
try (Scanner scanner = new Scanner(System.in)) { try (Scanner scanner = new Scanner(System.in)) {
boolean exit = false; boolean exit = false;

View File

@ -25,30 +25,29 @@ package com.iluwatar.hexagonal.service;
import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.banking.WireTransfers;
import com.iluwatar.hexagonal.domain.LotteryService; import com.iluwatar.hexagonal.domain.LotteryService;
import java.util.Scanner; import java.util.Scanner;
/** /**
* Console interface for lottery service * Console interface for lottery service.
*/ */
public interface LotteryConsoleService { public interface LotteryConsoleService {
void checkTicket(LotteryService service, Scanner scanner); void checkTicket(LotteryService service, Scanner scanner);
/** /**
* Submit lottery ticket to participate in the lottery * Submit lottery ticket to participate in the lottery.
*/ */
void submitTicket(LotteryService service, Scanner scanner); void submitTicket(LotteryService service, Scanner scanner);
/** /**
* Add funds to lottery account * Add funds to lottery account.
*/ */
void addFundsToLotteryAccount(WireTransfers bank, Scanner scanner); void addFundsToLotteryAccount(WireTransfers bank, Scanner scanner);
/** /**
* Recovery funds from lottery account * Recovery funds from lottery account.
*/ */
void queryLotteryAccountFunds(WireTransfers bank, Scanner scanner); void queryLotteryAccountFunds(WireTransfers bank, Scanner scanner);

View File

@ -24,22 +24,29 @@
package com.iluwatar.hexagonal.service; package com.iluwatar.hexagonal.service;
import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.banking.WireTransfers;
import com.iluwatar.hexagonal.domain.*; import com.iluwatar.hexagonal.domain.LotteryNumbers;
import com.iluwatar.hexagonal.domain.LotteryService;
import com.iluwatar.hexagonal.domain.LotteryTicket;
import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult;
import com.iluwatar.hexagonal.domain.LotteryTicketId;
import com.iluwatar.hexagonal.domain.PlayerDetails;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Optional;
import java.util.Scanner;
import java.util.Set;
import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/** /**
* Console implementation for lottery console service * Console implementation for lottery console service.
*/ */
public class LotteryConsoleServiceImpl implements LotteryConsoleService { public class LotteryConsoleServiceImpl implements LotteryConsoleService {
private final Logger logger; private final Logger logger;
/** /**
* Constructor * Constructor.
*/ */
public LotteryConsoleServiceImpl(Logger logger) { public LotteryConsoleServiceImpl(Logger logger) {
this.logger = logger; this.logger = logger;
@ -47,79 +54,81 @@ public class LotteryConsoleServiceImpl implements LotteryConsoleService {
@Override @Override
public void checkTicket(LotteryService service, Scanner scanner) { public void checkTicket(LotteryService service, Scanner scanner) {
logger.info( "What is the ID of the lottery ticket?" ); logger.info("What is the ID of the lottery ticket?");
String id = readString( scanner ); String id = readString(scanner);
logger.info( "Give the 4 comma separated winning numbers?" ); logger.info("Give the 4 comma separated winning numbers?");
String numbers = readString( scanner ); String numbers = readString(scanner);
try { try {
String[] parts = numbers.split( "," ); String[] parts = numbers.split(",");
Set<Integer> winningNumbers = new HashSet<>(); Set<Integer> winningNumbers = new HashSet<>();
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
winningNumbers.add( Integer.parseInt( parts[i] ) ); winningNumbers.add(Integer.parseInt(parts[i]));
} }
final LotteryTicketId lotteryTicketId = new LotteryTicketId( Integer.parseInt( id ) ); final LotteryTicketId lotteryTicketId = new LotteryTicketId(Integer.parseInt(id));
final LotteryNumbers lotteryNumbers = LotteryNumbers.create( winningNumbers ); final LotteryNumbers lotteryNumbers = LotteryNumbers.create(winningNumbers);
LotteryTicketCheckResult result = service.checkTicketForPrize( lotteryTicketId, lotteryNumbers ); LotteryTicketCheckResult result =
service.checkTicketForPrize(lotteryTicketId, lotteryNumbers);
if (result.getResult().equals( LotteryTicketCheckResult.CheckResult.WIN_PRIZE )) { if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) {
logger.info( "Congratulations! The lottery ticket has won!" ); logger.info("Congratulations! The lottery ticket has won!");
} else if (result.getResult().equals( LotteryTicketCheckResult.CheckResult.NO_PRIZE )) { } else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) {
logger.info( "Unfortunately the lottery ticket did not win." ); logger.info("Unfortunately the lottery ticket did not win.");
} else { } else {
logger.info( "Such lottery ticket has not been submitted." ); logger.info("Such lottery ticket has not been submitted.");
} }
} catch (Exception e) { } catch (Exception e) {
logger.info( "Failed checking the lottery ticket - please try again." ); logger.info("Failed checking the lottery ticket - please try again.");
} }
} }
@Override @Override
public void submitTicket(LotteryService service, Scanner scanner) { public void submitTicket(LotteryService service, Scanner scanner) {
logger.info( "What is your email address?" ); logger.info("What is your email address?");
String email = readString( scanner ); String email = readString(scanner);
logger.info( "What is your bank account number?" ); logger.info("What is your bank account number?");
String account = readString( scanner ); String account = readString(scanner);
logger.info( "What is your phone number?" ); logger.info("What is your phone number?");
String phone = readString( scanner ); String phone = readString(scanner);
PlayerDetails details = new PlayerDetails( email, account, phone ); PlayerDetails details = new PlayerDetails(email, account, phone);
logger.info( "Give 4 comma separated lottery numbers?" ); logger.info("Give 4 comma separated lottery numbers?");
String numbers = readString( scanner ); String numbers = readString(scanner);
try { try {
String[] parts = numbers.split( "," ); String[] parts = numbers.split(",");
Set<Integer> chosen = Arrays.stream(parts).map(Integer::parseInt).collect(Collectors.toSet()); Set<Integer> chosen = Arrays.stream(parts).map(Integer::parseInt).collect(Collectors.toSet());
LotteryNumbers lotteryNumbers = LotteryNumbers.create( chosen ); LotteryNumbers lotteryNumbers = LotteryNumbers.create(chosen);
LotteryTicket lotteryTicket = new LotteryTicket( new LotteryTicketId(), details, lotteryNumbers ); LotteryTicket lotteryTicket =
Optional<LotteryTicketId> id = service.submitTicket( lotteryTicket ); new LotteryTicket(new LotteryTicketId(), details, lotteryNumbers);
Optional<LotteryTicketId> id = service.submitTicket(lotteryTicket);
if (id.isPresent()) { if (id.isPresent()) {
logger.info( "Submitted lottery ticket with id: {}", id.get() ); logger.info("Submitted lottery ticket with id: {}", id.get());
} else { } else {
logger.info( "Failed submitting lottery ticket - please try again." ); logger.info("Failed submitting lottery ticket - please try again.");
} }
} catch (Exception e) { } catch (Exception e) {
logger.info( "Failed submitting lottery ticket - please try again." ); logger.info("Failed submitting lottery ticket - please try again.");
} }
} }
@Override @Override
public void addFundsToLotteryAccount(WireTransfers bank, Scanner scanner) { public void addFundsToLotteryAccount(WireTransfers bank, Scanner scanner) {
logger.info( "What is the account number?" ); logger.info("What is the account number?");
String account = readString( scanner ); String account = readString(scanner);
logger.info( "How many credits do you want to deposit?" ); logger.info("How many credits do you want to deposit?");
String amount = readString( scanner ); String amount = readString(scanner);
bank.setFunds( account, Integer.parseInt( amount ) ); bank.setFunds(account, Integer.parseInt(amount));
logger.info( "The account {} now has {} credits.", account, bank.getFunds( account ) ); logger.info("The account {} now has {} credits.", account, bank.getFunds(account));
} }
@Override @Override
public void queryLotteryAccountFunds(WireTransfers bank, Scanner scanner) { public void queryLotteryAccountFunds(WireTransfers bank, Scanner scanner) {
logger.info( "What is the account number?" ); logger.info("What is the account number?");
String account = readString( scanner ); String account = readString(scanner);
logger.info( "The account {} has {} credits.", account, bank.getFunds( account ) ); logger.info("The account {} has {} credits.", account, bank.getFunds(account));
} }
private String readString(Scanner scanner) { private String readString(Scanner scanner) {
logger.info( "> " ); logger.info("> ");
return scanner.next(); return scanner.next();
} }
} }