Resolves checkstyle errors for template-method thread-pool throttling tls tolerant-reader (#1073)

* Reduces checkstyle errors in template-method

* Reduces checkstyle errors in thread-pool

* Reduces checkstyle errors in throttling

* Reduces checkstyle errors in tls

* Reduces checkstyle errors in tolerant-reader
This commit is contained in:
Anurag Agarwal 2019-11-10 23:15:17 +05:30 committed by Ilkka Seppälä
parent 9c8ad4485b
commit b92eb5229d
23 changed files with 176 additions and 223 deletions

View File

@ -24,19 +24,17 @@
package com.iluwatar.templatemethod; package com.iluwatar.templatemethod;
/** /**
*
* Template Method defines a skeleton for an algorithm. The algorithm subclasses provide * Template Method defines a skeleton for an algorithm. The algorithm subclasses provide
* implementation for the blank parts. * implementation for the blank parts.
* <p> *
* In this example {@link HalflingThief} contains {@link StealingMethod} that can be changed. First * <p>In this example {@link HalflingThief} contains {@link StealingMethod} that can be changed.
* the thief hits with {@link HitAndRunMethod} and then with {@link SubtleMethod}. * First the thief hits with {@link HitAndRunMethod} and then with {@link SubtleMethod}.
*
*/ */
public class App { public class App {
/** /**
* Program entry point * Program entry point.
* *
* @param args command line args * @param args command line args
*/ */
public static void main(String[] args) { public static void main(String[] args) {

View File

@ -24,9 +24,7 @@
package com.iluwatar.templatemethod; package com.iluwatar.templatemethod;
/** /**
*
* Halfling thief uses {@link StealingMethod} to steal. * Halfling thief uses {@link StealingMethod} to steal.
*
*/ */
public class HalflingThief { public class HalflingThief {

View File

@ -27,9 +27,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
*
* HitAndRunMethod implementation of {@link StealingMethod}. * HitAndRunMethod implementation of {@link StealingMethod}.
*
*/ */
public class HitAndRunMethod extends StealingMethod { public class HitAndRunMethod extends StealingMethod {

View File

@ -27,9 +27,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
*
* StealingMethod defines skeleton for the algorithm. * StealingMethod defines skeleton for the algorithm.
*
*/ */
public abstract class StealingMethod { public abstract class StealingMethod {
@ -42,7 +40,7 @@ public abstract class StealingMethod {
protected abstract void stealTheItem(String target); protected abstract void stealTheItem(String target);
/** /**
* Steal * Steal.
*/ */
public void steal() { public void steal() {
var target = pickTarget(); var target = pickTarget();

View File

@ -27,9 +27,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
*
* SubtleMethod implementation of {@link StealingMethod}. * SubtleMethod implementation of {@link StealingMethod}.
*
*/ */
public class SubtleMethod extends StealingMethod { public class SubtleMethod extends StealingMethod {

View File

@ -23,36 +23,32 @@
package com.iluwatar.threadpool; package com.iluwatar.threadpool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
*
* Thread Pool pattern is where a number of threads are created to perform a number of tasks, which * Thread Pool pattern is where a number of threads are created to perform a number of tasks, which
* are usually organized in a queue. The results from the tasks being executed might also be placed * are usually organized in a queue. The results from the tasks being executed might also be placed
* in a queue, or the tasks might return no result. Typically, there are many more tasks than * in a queue, or the tasks might return no result. Typically, there are many more tasks than
* threads. As soon as a thread completes its task, it will request the next task from the queue * threads. As soon as a thread completes its task, it will request the next task from the queue
* until all tasks have been completed. The thread can then terminate, or sleep until there are new * until all tasks have been completed. The thread can then terminate, or sleep until there are new
* tasks available. * tasks available.
* <p>
* In this example we create a list of tasks presenting work to be done. Each task is then wrapped
* into a {@link Worker} object that implements {@link Runnable}. We create an
* {@link ExecutorService} with fixed number of threads (Thread Pool) and use them to execute the
* {@link Worker}s.
* *
* <p>In this example we create a list of tasks presenting work to be done. Each task is then
* wrapped into a {@link Worker} object that implements {@link Runnable}. We create an {@link
* ExecutorService} with fixed number of threads (Thread Pool) and use them to execute the {@link
* Worker}s.
*/ */
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
*/ */
public static void main(String[] args) { public static void main(String[] args) {
@ -61,21 +57,21 @@ public class App {
// Create a list of tasks to be executed // Create a list of tasks to be executed
List<Task> tasks = List.of( List<Task> tasks = List.of(
new PotatoPeelingTask(3), new PotatoPeelingTask(3),
new PotatoPeelingTask(6), new PotatoPeelingTask(6),
new CoffeeMakingTask(2), new CoffeeMakingTask(2),
new CoffeeMakingTask(6), new CoffeeMakingTask(6),
new PotatoPeelingTask(4), new PotatoPeelingTask(4),
new CoffeeMakingTask(2), new CoffeeMakingTask(2),
new PotatoPeelingTask(4), new PotatoPeelingTask(4),
new CoffeeMakingTask(9), new CoffeeMakingTask(9),
new PotatoPeelingTask(3), new PotatoPeelingTask(3),
new CoffeeMakingTask(2), new CoffeeMakingTask(2),
new PotatoPeelingTask(4), new PotatoPeelingTask(4),
new CoffeeMakingTask(2), new CoffeeMakingTask(2),
new CoffeeMakingTask(7), new CoffeeMakingTask(7),
new PotatoPeelingTask(4), new PotatoPeelingTask(4),
new PotatoPeelingTask(5)); new PotatoPeelingTask(5));
// Creates a thread pool that reuses a fixed number of threads operating off a shared // Creates a thread pool that reuses a fixed number of threads operating off a shared
// unbounded queue. At any point, at most nThreads threads will be active processing // unbounded queue. At any point, at most nThreads threads will be active processing

View File

@ -24,9 +24,7 @@
package com.iluwatar.threadpool; package com.iluwatar.threadpool;
/** /**
* * CoffeeMakingTask is a concrete task.
* CoffeeMakingTask is a concrete task
*
*/ */
public class CoffeeMakingTask extends Task { public class CoffeeMakingTask extends Task {

View File

@ -24,9 +24,7 @@
package com.iluwatar.threadpool; package com.iluwatar.threadpool;
/** /**
* * PotatoPeelingTask is a concrete task.
* PotatoPeelingTask is a concrete task
*
*/ */
public class PotatoPeelingTask extends Task { public class PotatoPeelingTask extends Task {

View File

@ -26,9 +26,7 @@ package com.iluwatar.threadpool;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
/** /**
* * Abstract base class for tasks.
* Abstract base class for tasks
*
*/ */
public abstract class Task { public abstract class Task {

View File

@ -27,9 +27,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* * Worker implements {@link Runnable} and thus can be executed by {@link
* Worker implements {@link Runnable} and thus can be executed by {@link java.util.concurrent.ExecutorService} * java.util.concurrent.ExecutorService}.
*
*/ */
public class Worker implements Runnable { public class Worker implements Runnable {

View File

@ -23,33 +23,30 @@
package com.iluwatar.throttling; package com.iluwatar.throttling;
import com.iluwatar.throttling.timer.ThrottleTimerImpl;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.iluwatar.throttling.timer.Throttler;
import com.iluwatar.throttling.timer.ThrottleTimerImpl;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/** /**
* Throttling pattern is a design pattern to throttle or limit the use of resources or even a complete service by * Throttling pattern is a design pattern to throttle or limit the use of resources or even a
* users or a particular tenant. This can allow systems to continue to function and meet service level agreements, * complete service by users or a particular tenant. This can allow systems to continue to function
* even when an increase in demand places load on resources. * and meet service level agreements, even when an increase in demand places load on resources.
* <p> * <p>
* In this example we have ({@link App}) as the initiating point of the service. * In this example we have ({@link App}) as the initiating point of the service. This is a time
* This is a time based throttling, i.e. only a certain number of calls are allowed per second. * based throttling, i.e. only a certain number of calls are allowed per second.
* </p> * </p>
* ({@link Tenant}) is the Tenant POJO class with which many tenants can be created * ({@link Tenant}) is the Tenant POJO class with which many tenants can be created ({@link
* ({@link B2BService}) is the service which is consumed by the tenants and is throttled. * B2BService}) is the service which is consumed by the tenants and is throttled.
*/ */
public class App { public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class); private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/** /**
* Application entry point * Application entry point.
*
* @param args main arguments * @param args main arguments
*/ */
public static void main(String[] args) { public static void main(String[] args) {
@ -58,10 +55,10 @@ public class App {
var nike = new Tenant("Nike", 6, callsCount); var nike = new Tenant("Nike", 6, callsCount);
var executorService = Executors.newFixedThreadPool(2); var executorService = Executors.newFixedThreadPool(2);
executorService.execute(() -> makeServiceCalls(adidas, callsCount)); executorService.execute(() -> makeServiceCalls(adidas, callsCount));
executorService.execute(() -> makeServiceCalls(nike, callsCount)); executorService.execute(() -> makeServiceCalls(nike, callsCount));
executorService.shutdown(); executorService.shutdown();
try { try {
executorService.awaitTermination(10, TimeUnit.SECONDS); executorService.awaitTermination(10, TimeUnit.SECONDS);
@ -71,14 +68,14 @@ public class App {
} }
/** /**
* Make calls to the B2BService dummy API * Make calls to the B2BService dummy API.
*/ */
private static void makeServiceCalls(Tenant tenant, CallsCount callsCount) { private static void makeServiceCalls(Tenant tenant, CallsCount callsCount) {
var timer = new ThrottleTimerImpl(10, callsCount); var timer = new ThrottleTimerImpl(10, callsCount);
var service = new B2BService(timer, callsCount); var service = new B2BService(timer, callsCount);
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
service.dummyCustomerApi(tenant); service.dummyCustomerApi(tenant);
// Sleep is introduced to keep the output in check and easy to view and analyze the results. // Sleep is introduced to keep the output in check and easy to view and analyze the results.
try { try {
Thread.sleep(1); Thread.sleep(1);
} catch (InterruptedException e) { } catch (InterruptedException e) {

View File

@ -23,15 +23,14 @@
package com.iluwatar.throttling; package com.iluwatar.throttling;
import com.iluwatar.throttling.timer.Throttler;
import java.util.concurrent.ThreadLocalRandom;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.iluwatar.throttling.timer.Throttler;
import java.util.concurrent.ThreadLocalRandom;
/** /**
* A service which accepts a tenant and throttles the resource based on the time given to the tenant. * A service which accepts a tenant and throttles the resource based on the time given to the
* tenant.
*/ */
class B2BService { class B2BService {
@ -44,6 +43,7 @@ class B2BService {
} }
/** /**
* Calls dummy customer api.
* *
* @return customer id which is randomly generated * @return customer id which is randomly generated
*/ */

View File

@ -23,18 +23,17 @@
package com.iluwatar.throttling; package com.iluwatar.throttling;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* A class to keep track of the counter of different Tenants * A class to keep track of the counter of different Tenants.
* @author drastogi
* *
* @author drastogi
*/ */
public final class CallsCount { public final class CallsCount {
@ -43,29 +42,32 @@ public final class CallsCount {
/** /**
* Add a new tenant to the map. * Add a new tenant to the map.
*
* @param tenantName name of the tenant. * @param tenantName name of the tenant.
*/ */
public void addTenant(String tenantName) { public void addTenant(String tenantName) {
tenantCallsCount.putIfAbsent(tenantName, new AtomicLong(0)); tenantCallsCount.putIfAbsent(tenantName, new AtomicLong(0));
} }
/** /**
* Increment the count of the specified tenant. * Increment the count of the specified tenant.
*
* @param tenantName name of the tenant. * @param tenantName name of the tenant.
*/ */
public void incrementCount(String tenantName) { public void incrementCount(String tenantName) {
tenantCallsCount.get(tenantName).incrementAndGet(); tenantCallsCount.get(tenantName).incrementAndGet();
} }
/** /**
* * Get count of tenant based on tenant name.
*
* @param tenantName name of the tenant. * @param tenantName name of the tenant.
* @return the count of the tenant. * @return the count of the tenant.
*/ */
public long getCount(String tenantName) { public long getCount(String tenantName) {
return tenantCallsCount.get(tenantName).get(); return tenantCallsCount.get(tenantName).get();
} }
/** /**
* Resets the count of all the tenants in the map. * Resets the count of all the tenants in the map.
*/ */

View File

@ -34,8 +34,9 @@ public class Tenant {
private int allowedCallsPerSecond; private int allowedCallsPerSecond;
/** /**
* Constructor.
* *
* @param name Name of the tenant * @param name Name of the tenant
* @param allowedCallsPerSecond The number of calls allowed for a particular tenant. * @param allowedCallsPerSecond The number of calls allowed for a particular tenant.
* @throws InvalidParameterException If number of calls is less than 0, throws exception. * @throws InvalidParameterException If number of calls is less than 0, throws exception.
*/ */

View File

@ -21,16 +21,12 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
/**
*
*/
package com.iluwatar.throttling.timer; package com.iluwatar.throttling.timer;
import com.iluwatar.throttling.CallsCount;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import com.iluwatar.throttling.CallsCount;
/** /**
* Implementation of throttler interface. This class resets the counter every second. * Implementation of throttler interface. This class resets the counter every second.
* @author drastogi * @author drastogi
@ -45,7 +41,7 @@ public class ThrottleTimerImpl implements Throttler {
this.throttlePeriod = throttlePeriod; this.throttlePeriod = throttlePeriod;
this.callsCount = callsCount; this.callsCount = callsCount;
} }
/** /**
* A timer is initiated with this method. The timer runs every second and resets the * A timer is initiated with this method. The timer runs every second and resets the
* counter. * counter.

View File

@ -21,9 +21,6 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
/**
*
*/
package com.iluwatar.throttling.timer; package com.iluwatar.throttling.timer;
/** /**
@ -32,6 +29,6 @@ package com.iluwatar.throttling.timer;
* *
*/ */
public interface Throttler { public interface Throttler {
void start(); void start();
} }

View File

@ -23,58 +23,52 @@
package com.iluwatar.tls; package com.iluwatar.tls;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* ThreadLocal pattern * ThreadLocal pattern
* <p> *
* This App shows how to create an isolated space per each thread. In this * <p>This App shows how to create an isolated space per each thread. In this example the usage of
* example the usage of SimpleDateFormat is made to be thread-safe. This is an * SimpleDateFormat is made to be thread-safe. This is an example of the ThreadLocal pattern.
* example of the ThreadLocal pattern. *
* <p> * <p>By applying the ThreadLocal pattern you can keep track of application instances or locale
* By applying the ThreadLocal pattern you can keep track of application * settings throughout the handling of a request. The ThreadLocal class works like a static
* instances or locale settings throughout the handling of a request. The * variable, with the exception that it is only bound to the current thread! This allows us to use
* ThreadLocal class works like a static variable, with the exception that it is * static variables in a thread-safe way.
* only bound to the current thread! This allows us to use static variables in a *
* thread-safe way. * <p>In Java, thread-local variables are implemented by the ThreadLocal class object. ThreadLocal
* <p> * holds a variable of type T, which is accessible via get/set methods.
* In Java, thread-local variables are implemented by the ThreadLocal class *
* object. ThreadLocal holds a variable of type T, which is accessible via get/set * <p>SimpleDateFormat is one of the basic Java classes and is not thread-safe. If you do not
* methods. * isolate the instance of SimpleDateFormat per each thread then problems arise.
* <p> *
* SimpleDateFormat is one of the basic Java classes and is not thread-safe. If * <p>App converts the String date value 15/12/2015 to the Date format using the Java class
* you do not isolate the instance of SimpleDateFormat per each thread then * SimpleDateFormat. It does this 20 times using 4 threads, each doing it 5 times. With the usage of
* problems arise. * as ThreadLocal in DateFormatCallable everything runs well. But if you comment out the ThreadLocal
* <p> * variant (marked with "//TLTL") and comment in the non ThreadLocal variant (marked with
* App converts the String date value 15/12/2015 to the Date format using the * "//NTLNTL") you can see what will happen without the ThreadLocal. Most likely you will get
* Java class SimpleDateFormat. It does this 20 times using 4 threads, each doing * incorrect date values and / or exceptions.
* it 5 times. With the usage of as ThreadLocal in DateFormatCallable everything *
* runs well. But if you comment out the ThreadLocal variant (marked with "//TLTL") * <p>This example clearly show what will happen when using non thread-safe classes in a thread. In
* and comment in the non ThreadLocal variant (marked with "//NTLNTL") you can * real life this may happen one in of 1.000 or 10.000 conversions and those are really hard to find
* see what will happen without the ThreadLocal. Most likely you will get incorrect * errors.
* date values and / or exceptions. *
* <p> * @author Thomas Bauer, 2017
* This example clearly show what will happen when using non thread-safe classes
* in a thread. In real life this may happen one in of 1.000 or 10.000 conversions
* and those are really hard to find errors.
*
* @author Thomas Bauer, 2017
*/ */
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 * @param args command line args
* command line args
*/ */
public static void main(String[] args) { public static void main(String[] args) {
int counterDateValues = 0; int counterDateValues = 0;
@ -115,9 +109,9 @@ public class App {
} }
/** /**
* Print result (date values) of a thread execution and count dates * Print result (date values) of a thread execution and count dates.
* *
* @param res contains results of a thread execution * @param res contains results of a thread execution
*/ */
private static int printAndCountDates(Result res) { private static int printAndCountDates(Result res) {
// a correct run should deliver 5 times 15.12.2015 per each thread // a correct run should deliver 5 times 15.12.2015 per each thread
@ -128,15 +122,16 @@ public class App {
cal.setTime(dt); cal.setTime(dt);
// Formatted output of the date value: DD.MM.YYYY // Formatted output of the date value: DD.MM.YYYY
LOGGER.info( LOGGER.info(
cal.get(Calendar.DAY_OF_MONTH) + "." + cal.get(Calendar.MONTH) + "." + +cal.get(Calendar.YEAR)); cal.get(Calendar.DAY_OF_MONTH) + "." + cal.get(Calendar.MONTH) + "." + +cal
.get(Calendar.YEAR));
} }
return counter; return counter;
} }
/** /**
* Print result (exceptions) of a thread execution and count exceptions * Print result (exceptions) of a thread execution and count exceptions.
* *
* @param res contains results of a thread execution * @param res contains results of a thread execution
* @return number of dates * @return number of dates
*/ */
private static int printAndCountExceptions(Result res) { private static int printAndCountExceptions(Result res) {

View File

@ -23,25 +23,23 @@
package com.iluwatar.tls; package com.iluwatar.tls;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* DateFormatCallable converts string dates to a date format using * DateFormatCallable converts string dates to a date format using SimpleDateFormat. The date format
* SimpleDateFormat. The date format and the date value will be passed to the * and the date value will be passed to the Callable by the constructor. The constructor creates a
* Callable by the constructor. The constructor creates a instance of * instance of SimpleDateFormat and stores it in a ThreadLocal class variable. For the complete
* SimpleDateFormat and stores it in a ThreadLocal class variable. For the * description of the example see {@link App}.
* complete description of the example see {@link App}
*
* You can comment out the code marked with //TLTL and comment in the
* code marked //NTLNTL. Then you can see what will happen if you do not
* use the ThreadLocal. For details see the description of {@link App}
* *
* @author Thomas Bauer, 2017 * <p>You can comment out the code marked with //TLTL and comment in the code marked //NTLNTL. Then
* you can see what will happen if you do not use the ThreadLocal. For details see the description
* of {@link App}
*
* @author Thomas Bauer, 2017
*/ */
public class DateFormatCallable implements Callable<Result> { public class DateFormatCallable implements Callable<Result> {
@ -51,15 +49,13 @@ public class DateFormatCallable implements Callable<Result> {
// private DateFormat df; //NTLNTL // private DateFormat df; //NTLNTL
private String dateValue; // for dateValue Thread Local not needed private String dateValue; // for dateValue Thread Local not needed
/** /**
* The date format and the date value are passed to the constructor * The date format and the date value are passed to the constructor.
* *
* @param inDateFormat * @param inDateFormat string date format string, e.g. "dd/MM/yyyy"
* string date format string, e.g. "dd/MM/yyyy" * @param inDateValue string date value, e.g. "21/06/2016"
* @param inDateValue
* string date value, e.g. "21/06/2016"
*/ */
public DateFormatCallable(String inDateFormat, String inDateValue) { public DateFormatCallable(String inDateFormat, String inDateValue) {
final String idf = inDateFormat; //TLTL final String idf = inDateFormat; //TLTL
@ -73,9 +69,6 @@ public class DateFormatCallable implements Callable<Result> {
this.dateValue = inDateValue; this.dateValue = inDateValue;
} }
/**
* @see java.util.concurrent.Callable#call()
*/
@Override @Override
public Result call() { public Result call() {
LOGGER.info(Thread.currentThread() + " started executing..."); LOGGER.info(Thread.currentThread() + " started executing...");
@ -88,7 +81,7 @@ public class DateFormatCallable implements Callable<Result> {
// instance of SimpleDateFormat locally // instance of SimpleDateFormat locally
// Create the date value and store it in dateList // Create the date value and store it in dateList
result.getDateList().add(this.df.get().parse(this.dateValue)); //TLTL result.getDateList().add(this.df.get().parse(this.dateValue)); //TLTL
// result.getDateList().add(this.df.parse(this.dateValue)); //NTLNTL // result.getDateList().add(this.df.parse(this.dateValue)); //NTLNTL
} catch (Exception e) { } catch (Exception e) {
// write the Exception to a list and continue work // write the Exception to a list and continue work
result.getExceptionList().add(e.getClass() + ": " + e.getMessage()); result.getExceptionList().add(e.getClass() + ": " + e.getMessage());

View File

@ -32,10 +32,10 @@ import java.util.Date;
import java.util.List; import java.util.List;
/** /**
* Result object that will be returned by the Callable {@link DateFormatCallable} * Result object that will be returned by the Callable {@link DateFormatCallable} used in {@link
* used in {@link App} * App}.
* *
* @author Thomas Bauer, 2017 * @author Thomas Bauer, 2017
*/ */
public class Result { public class Result {
// A list to collect the date values created in one thread // A list to collect the date values created in one thread
@ -44,9 +44,10 @@ public class Result {
// A list to collect Exceptions thrown in one threads (should be none in // A list to collect Exceptions thrown in one threads (should be none in
// this example) // this example)
private List<String> exceptionList = new ArrayList<String>(); private List<String> exceptionList = new ArrayList<String>();
/** /**
* * Get list of date values collected within a thread execution.
*
* @return List of date values collected within an thread execution * @return List of date values collected within an thread execution
*/ */
public List<Date> getDateList() { public List<Date> getDateList() {
@ -54,7 +55,8 @@ public class Result {
} }
/** /**
* * Get list of exceptions thrown within a thread execution.
*
* @return List of exceptions thrown within an thread execution * @return List of exceptions thrown within an thread execution
*/ */
public List<String> getExceptionList() { public List<String> getExceptionList() {

View File

@ -23,32 +23,29 @@
package com.iluwatar.tolerantreader; package com.iluwatar.tolerantreader;
import java.io.IOException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException;
/** /**
*
* Tolerant Reader is an integration pattern that helps creating robust communication systems. The * Tolerant Reader is an integration pattern that helps creating robust communication systems. The
* idea is to be as tolerant as possible when reading data from another service. This way, when the * idea is to be as tolerant as possible when reading data from another service. This way, when the
* communication schema changes, the readers must not break. * communication schema changes, the readers must not break.
* <p>
* In this example we use Java serialization to write representations of {@link RainbowFish} objects
* to file. {@link RainbowFish} is the initial version which we can easily read and write using
* {@link RainbowFishSerializer} methods. {@link RainbowFish} then evolves to {@link RainbowFishV2}
* and we again write it to file with a method designed to do just that. However, the reader client
* does not know about the new format and still reads with the method designed for V1 schema.
* Fortunately the reading method has been designed with the Tolerant Reader pattern and does not
* break even though {@link RainbowFishV2} has new fields that are serialized.
* *
* <p>In this example we use Java serialization to write representations of {@link RainbowFish}
* objects to file. {@link RainbowFish} is the initial version which we can easily read and write
* using {@link RainbowFishSerializer} methods. {@link RainbowFish} then evolves to {@link
* RainbowFishV2} and we again write it to file with a method designed to do just that. However, the
* reader client does not know about the new format and still reads with the method designed for V1
* schema. Fortunately the reading method has been designed with the Tolerant Reader pattern and
* does not break even though {@link RainbowFishV2} has new fields that are serialized.
*/ */
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.
*/ */
public static void main(String[] args) throws IOException, ClassNotFoundException { public static void main(String[] args) throws IOException, ClassNotFoundException {
// Write V1 // Write V1
@ -59,8 +56,8 @@ public class App {
// Read V1 // Read V1
var deserializedRainbowFishV1 = RainbowFishSerializer.readV1("fish1.out"); var deserializedRainbowFishV1 = RainbowFishSerializer.readV1("fish1.out");
LOGGER.info("deserializedFishV1 name={} age={} length={} weight={}", LOGGER.info("deserializedFishV1 name={} age={} length={} weight={}",
deserializedRainbowFishV1.getName(), deserializedRainbowFishV1.getAge(), deserializedRainbowFishV1.getName(), deserializedRainbowFishV1.getAge(),
deserializedRainbowFishV1.getLengthMeters(), deserializedRainbowFishV1.getWeightTons()); deserializedRainbowFishV1.getLengthMeters(), deserializedRainbowFishV1.getWeightTons());
// Write V2 // Write V2
var fishV2 = new RainbowFishV2("Scar", 5, 12, 15, true, true, true); var fishV2 = new RainbowFishV2("Scar", 5, 12, 15, true, true, true);
LOGGER.info( LOGGER.info(

View File

@ -26,9 +26,7 @@ package com.iluwatar.tolerantreader;
import java.io.Serializable; import java.io.Serializable;
/** /**
* * RainbowFish is the initial schema.
* RainbowFish is the initial schema
*
*/ */
public class RainbowFish implements Serializable { public class RainbowFish implements Serializable {
@ -40,7 +38,7 @@ public class RainbowFish implements Serializable {
private int weightTons; private int weightTons;
/** /**
* Constructor * Constructor.
*/ */
public RainbowFish(String name, int age, int lengthMeters, int weightTons) { public RainbowFish(String name, int age, int lengthMeters, int weightTons) {
this.name = name; this.name = name;

View File

@ -28,16 +28,13 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
*
* RainbowFishSerializer provides methods for reading and writing {@link RainbowFish} objects to * RainbowFishSerializer provides methods for reading and writing {@link RainbowFish} objects to
* file. Tolerant Reader pattern is implemented here by serializing maps instead of * file. Tolerant Reader pattern is implemented here by serializing maps instead of {@link
* {@link RainbowFish} objects. This way the reader does not break even though new properties are * RainbowFish} objects. This way the reader does not break even though new properties are added to
* added to the schema. * the schema.
*
*/ */
public final class RainbowFishSerializer { public final class RainbowFishSerializer {
@ -45,54 +42,55 @@ public final class RainbowFishSerializer {
} }
/** /**
* Write V1 RainbowFish to file * Write V1 RainbowFish to file.
*/ */
public static void writeV1(RainbowFish rainbowFish, String filename) throws IOException { public static void writeV1(RainbowFish rainbowFish, String filename) throws IOException {
var map = Map.of( var map = Map.of(
"name", rainbowFish.getName(), "name", rainbowFish.getName(),
"age", String.format("%d", rainbowFish.getAge()), "age", String.format("%d", rainbowFish.getAge()),
"lengthMeters", String.format("%d", rainbowFish.getLengthMeters()), "lengthMeters", String.format("%d", rainbowFish.getLengthMeters()),
"weightTons", String.format("%d", rainbowFish.getWeightTons()) "weightTons", String.format("%d", rainbowFish.getWeightTons())
); );
try (var fileOut = new FileOutputStream(filename); try (var fileOut = new FileOutputStream(filename);
var objOut = new ObjectOutputStream(fileOut)) { var objOut = new ObjectOutputStream(fileOut)) {
objOut.writeObject(map); objOut.writeObject(map);
} }
} }
/** /**
* Write V2 RainbowFish to file * Write V2 RainbowFish to file.
*/ */
public static void writeV2(RainbowFishV2 rainbowFish, String filename) throws IOException { public static void writeV2(RainbowFishV2 rainbowFish, String filename) throws IOException {
var map = Map.of( var map = Map.of(
"name", rainbowFish.getName(), "name", rainbowFish.getName(),
"age", String.format("%d", rainbowFish.getAge()), "age", String.format("%d", rainbowFish.getAge()),
"lengthMeters", String.format("%d", rainbowFish.getLengthMeters()), "lengthMeters", String.format("%d", rainbowFish.getLengthMeters()),
"weightTons", String.format("%d", rainbowFish.getWeightTons()), "weightTons", String.format("%d", rainbowFish.getWeightTons()),
"angry", Boolean.toString(rainbowFish.getAngry()), "angry", Boolean.toString(rainbowFish.getAngry()),
"hungry", Boolean.toString(rainbowFish.getHungry()), "hungry", Boolean.toString(rainbowFish.getHungry()),
"sleeping", Boolean.toString(rainbowFish.getSleeping()) "sleeping", Boolean.toString(rainbowFish.getSleeping())
); );
try (var fileOut = new FileOutputStream(filename); try (var fileOut = new FileOutputStream(filename);
var objOut = new ObjectOutputStream(fileOut)) { var objOut = new ObjectOutputStream(fileOut)) {
objOut.writeObject(map); objOut.writeObject(map);
} }
} }
/** /**
* Read V1 RainbowFish from file * Read V1 RainbowFish from file.
*/ */
public static RainbowFish readV1(String filename) throws IOException, ClassNotFoundException { public static RainbowFish readV1(String filename) throws IOException, ClassNotFoundException {
Map<String, String> map = null; Map<String, String> map = null;
try (var fileIn = new FileInputStream(filename); try (var fileIn = new FileInputStream(filename);
var objIn = new ObjectInputStream(fileIn)) { var objIn = new ObjectInputStream(fileIn)) {
map = (Map<String, String>) objIn.readObject(); map = (Map<String, String>) objIn.readObject();
} }
return new RainbowFish(map.get("name"), Integer.parseInt(map.get("age")), Integer.parseInt(map.get("lengthMeters")), return new RainbowFish(map.get("name"), Integer.parseInt(map.get("age")), Integer
.parseInt(map.get("lengthMeters")),
Integer.parseInt(map.get("weightTons"))); Integer.parseInt(map.get("weightTons")));
} }
} }

View File

@ -24,9 +24,7 @@
package com.iluwatar.tolerantreader; package com.iluwatar.tolerantreader;
/** /**
* * RainbowFishV2 is the evolved schema.
* RainbowFishV2 is the evolved schema
*
*/ */
public class RainbowFishV2 extends RainbowFish { public class RainbowFishV2 extends RainbowFish {
@ -41,10 +39,10 @@ public class RainbowFishV2 extends RainbowFish {
} }
/** /**
* Constructor * Constructor.
*/ */
public RainbowFishV2(String name, int age, int lengthMeters, int weightTons, boolean sleeping, public RainbowFishV2(String name, int age, int lengthMeters, int weightTons, boolean sleeping,
boolean hungry, boolean angry) { boolean hungry, boolean angry) {
this(name, age, lengthMeters, weightTons); this(name, age, lengthMeters, weightTons);
this.sleeping = sleeping; this.sleeping = sleeping;
this.hungry = hungry; this.hungry = hungry;