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:
parent
9c8ad4485b
commit
b92eb5229d
@ -24,19 +24,17 @@
|
||||
package com.iluwatar.templatemethod;
|
||||
|
||||
/**
|
||||
*
|
||||
* Template Method defines a skeleton for an algorithm. The algorithm subclasses provide
|
||||
* implementation for the blank parts.
|
||||
* <p>
|
||||
* In this example {@link HalflingThief} contains {@link StealingMethod} that can be changed. First
|
||||
* the thief hits with {@link HitAndRunMethod} and then with {@link SubtleMethod}.
|
||||
*
|
||||
*
|
||||
* <p>In this example {@link HalflingThief} contains {@link StealingMethod} that can be changed.
|
||||
* First the thief hits with {@link HitAndRunMethod} and then with {@link SubtleMethod}.
|
||||
*/
|
||||
public class App {
|
||||
|
||||
/**
|
||||
* Program entry point
|
||||
*
|
||||
* Program entry point.
|
||||
*
|
||||
* @param args command line args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
|
@ -24,9 +24,7 @@
|
||||
package com.iluwatar.templatemethod;
|
||||
|
||||
/**
|
||||
*
|
||||
* Halfling thief uses {@link StealingMethod} to steal.
|
||||
*
|
||||
*/
|
||||
public class HalflingThief {
|
||||
|
||||
|
@ -27,9 +27,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* HitAndRunMethod implementation of {@link StealingMethod}.
|
||||
*
|
||||
*/
|
||||
public class HitAndRunMethod extends StealingMethod {
|
||||
|
||||
|
@ -27,9 +27,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* StealingMethod defines skeleton for the algorithm.
|
||||
*
|
||||
*/
|
||||
public abstract class StealingMethod {
|
||||
|
||||
@ -42,7 +40,7 @@ public abstract class StealingMethod {
|
||||
protected abstract void stealTheItem(String target);
|
||||
|
||||
/**
|
||||
* Steal
|
||||
* Steal.
|
||||
*/
|
||||
public void steal() {
|
||||
var target = pickTarget();
|
||||
|
@ -27,9 +27,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* SubtleMethod implementation of {@link StealingMethod}.
|
||||
*
|
||||
*/
|
||||
public class SubtleMethod extends StealingMethod {
|
||||
|
||||
|
@ -23,36 +23,32 @@
|
||||
|
||||
package com.iluwatar.threadpool;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
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
|
||||
* 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
|
||||
* 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
|
||||
* 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 {
|
||||
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
|
||||
|
||||
/**
|
||||
* Program entry point
|
||||
*
|
||||
* Program entry point.
|
||||
*
|
||||
* @param args command line args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
@ -61,21 +57,21 @@ public class App {
|
||||
|
||||
// Create a list of tasks to be executed
|
||||
List<Task> tasks = List.of(
|
||||
new PotatoPeelingTask(3),
|
||||
new PotatoPeelingTask(6),
|
||||
new CoffeeMakingTask(2),
|
||||
new CoffeeMakingTask(6),
|
||||
new PotatoPeelingTask(4),
|
||||
new CoffeeMakingTask(2),
|
||||
new PotatoPeelingTask(4),
|
||||
new CoffeeMakingTask(9),
|
||||
new PotatoPeelingTask(3),
|
||||
new CoffeeMakingTask(2),
|
||||
new PotatoPeelingTask(4),
|
||||
new CoffeeMakingTask(2),
|
||||
new CoffeeMakingTask(7),
|
||||
new PotatoPeelingTask(4),
|
||||
new PotatoPeelingTask(5));
|
||||
new PotatoPeelingTask(3),
|
||||
new PotatoPeelingTask(6),
|
||||
new CoffeeMakingTask(2),
|
||||
new CoffeeMakingTask(6),
|
||||
new PotatoPeelingTask(4),
|
||||
new CoffeeMakingTask(2),
|
||||
new PotatoPeelingTask(4),
|
||||
new CoffeeMakingTask(9),
|
||||
new PotatoPeelingTask(3),
|
||||
new CoffeeMakingTask(2),
|
||||
new PotatoPeelingTask(4),
|
||||
new CoffeeMakingTask(2),
|
||||
new CoffeeMakingTask(7),
|
||||
new PotatoPeelingTask(4),
|
||||
new PotatoPeelingTask(5));
|
||||
|
||||
// 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
|
||||
|
@ -24,9 +24,7 @@
|
||||
package com.iluwatar.threadpool;
|
||||
|
||||
/**
|
||||
*
|
||||
* CoffeeMakingTask is a concrete task
|
||||
*
|
||||
* CoffeeMakingTask is a concrete task.
|
||||
*/
|
||||
public class CoffeeMakingTask extends Task {
|
||||
|
||||
|
@ -24,9 +24,7 @@
|
||||
package com.iluwatar.threadpool;
|
||||
|
||||
/**
|
||||
*
|
||||
* PotatoPeelingTask is a concrete task
|
||||
*
|
||||
* PotatoPeelingTask is a concrete task.
|
||||
*/
|
||||
public class PotatoPeelingTask extends Task {
|
||||
|
||||
|
@ -26,9 +26,7 @@ package com.iluwatar.threadpool;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
*
|
||||
* Abstract base class for tasks
|
||||
*
|
||||
* Abstract base class for tasks.
|
||||
*/
|
||||
public abstract class Task {
|
||||
|
||||
|
@ -27,9 +27,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* Worker implements {@link Runnable} and thus can be executed by {@link java.util.concurrent.ExecutorService}
|
||||
*
|
||||
* Worker implements {@link Runnable} and thus can be executed by {@link
|
||||
* java.util.concurrent.ExecutorService}.
|
||||
*/
|
||||
public class Worker implements Runnable {
|
||||
|
||||
|
@ -23,33 +23,30 @@
|
||||
|
||||
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.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
|
||||
* users or a particular tenant. This can allow systems to continue to function and meet service level agreements,
|
||||
* even when an increase in demand places load on resources.
|
||||
* Throttling pattern is a design pattern to throttle or limit the use of resources or even a
|
||||
* complete service by users or a particular tenant. This can allow systems to continue to function
|
||||
* and meet service level agreements, even when an increase in demand places load on resources.
|
||||
* <p>
|
||||
* In this example we have ({@link App}) as the initiating point of the service.
|
||||
* This is a time based throttling, i.e. only a certain number of calls are allowed per second.
|
||||
* In this example we have ({@link App}) as the initiating point of the service. This is a time
|
||||
* based throttling, i.e. only a certain number of calls are allowed per second.
|
||||
* </p>
|
||||
* ({@link Tenant}) is the Tenant POJO class with which many tenants can be created
|
||||
* ({@link B2BService}) is the service which is consumed by the tenants and is throttled.
|
||||
* ({@link Tenant}) is the Tenant POJO class with which many tenants can be created ({@link
|
||||
* B2BService}) is the service which is consumed by the tenants and is throttled.
|
||||
*/
|
||||
public class App {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
|
||||
|
||||
/**
|
||||
* Application entry point
|
||||
* Application entry point.
|
||||
*
|
||||
* @param args main arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
@ -58,10 +55,10 @@ public class App {
|
||||
var nike = new Tenant("Nike", 6, callsCount);
|
||||
|
||||
var executorService = Executors.newFixedThreadPool(2);
|
||||
|
||||
|
||||
executorService.execute(() -> makeServiceCalls(adidas, callsCount));
|
||||
executorService.execute(() -> makeServiceCalls(nike, callsCount));
|
||||
|
||||
|
||||
executorService.shutdown();
|
||||
try {
|
||||
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) {
|
||||
var timer = new ThrottleTimerImpl(10, callsCount);
|
||||
var service = new B2BService(timer, callsCount);
|
||||
for (int i = 0; i < 20; i++) {
|
||||
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 {
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
|
@ -23,15 +23,14 @@
|
||||
|
||||
package com.iluwatar.throttling;
|
||||
|
||||
import com.iluwatar.throttling.timer.Throttler;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import org.slf4j.Logger;
|
||||
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 {
|
||||
|
||||
@ -44,6 +43,7 @@ class B2BService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls dummy customer api.
|
||||
*
|
||||
* @return customer id which is randomly generated
|
||||
*/
|
||||
|
@ -23,18 +23,17 @@
|
||||
|
||||
package com.iluwatar.throttling;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
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
|
||||
* @author drastogi
|
||||
* A class to keep track of the counter of different Tenants.
|
||||
*
|
||||
* @author drastogi
|
||||
*/
|
||||
public final class CallsCount {
|
||||
|
||||
@ -43,29 +42,32 @@ public final class CallsCount {
|
||||
|
||||
/**
|
||||
* Add a new tenant to the map.
|
||||
*
|
||||
* @param tenantName name of the tenant.
|
||||
*/
|
||||
public void addTenant(String tenantName) {
|
||||
tenantCallsCount.putIfAbsent(tenantName, new AtomicLong(0));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Increment the count of the specified tenant.
|
||||
*
|
||||
* @param tenantName name of the tenant.
|
||||
*/
|
||||
public void incrementCount(String tenantName) {
|
||||
tenantCallsCount.get(tenantName).incrementAndGet();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Get count of tenant based on tenant name.
|
||||
*
|
||||
* @param tenantName name of the tenant.
|
||||
* @return the count of the tenant.
|
||||
*/
|
||||
public long getCount(String tenantName) {
|
||||
return tenantCallsCount.get(tenantName).get();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resets the count of all the tenants in the map.
|
||||
*/
|
||||
|
@ -34,8 +34,9 @@ public class Tenant {
|
||||
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.
|
||||
* @throws InvalidParameterException If number of calls is less than 0, throws exception.
|
||||
*/
|
||||
|
@ -21,16 +21,12 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package com.iluwatar.throttling.timer;
|
||||
|
||||
import com.iluwatar.throttling.CallsCount;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import com.iluwatar.throttling.CallsCount;
|
||||
|
||||
/**
|
||||
* Implementation of throttler interface. This class resets the counter every second.
|
||||
* @author drastogi
|
||||
@ -45,7 +41,7 @@ public class ThrottleTimerImpl implements Throttler {
|
||||
this.throttlePeriod = throttlePeriod;
|
||||
this.callsCount = callsCount;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A timer is initiated with this method. The timer runs every second and resets the
|
||||
* counter.
|
||||
|
@ -21,9 +21,6 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package com.iluwatar.throttling.timer;
|
||||
|
||||
/**
|
||||
@ -32,6 +29,6 @@ package com.iluwatar.throttling.timer;
|
||||
*
|
||||
*/
|
||||
public interface Throttler {
|
||||
|
||||
|
||||
void start();
|
||||
}
|
||||
|
@ -23,58 +23,52 @@
|
||||
|
||||
package com.iluwatar.tls;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* ThreadLocal pattern
|
||||
* <p>
|
||||
* This App shows how to create an isolated space per each thread. In this
|
||||
* example the usage of SimpleDateFormat is made to be thread-safe. This is an
|
||||
* example of the ThreadLocal pattern.
|
||||
* <p>
|
||||
* By applying the ThreadLocal pattern you can keep track of application
|
||||
* instances or locale settings throughout the handling of a request. The
|
||||
* ThreadLocal class works like a static variable, with the exception that it is
|
||||
* 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 holds a variable of type T, which is accessible via get/set
|
||||
* methods.
|
||||
* <p>
|
||||
* SimpleDateFormat is one of the basic Java classes and is not thread-safe. If
|
||||
* you do not isolate the instance of SimpleDateFormat per each thread then
|
||||
* problems arise.
|
||||
* <p>
|
||||
* App converts the String date value 15/12/2015 to the Date format using the
|
||||
* Java class SimpleDateFormat. It does this 20 times using 4 threads, each doing
|
||||
* 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")
|
||||
* and comment in the non ThreadLocal variant (marked with "//NTLNTL") you can
|
||||
* see what will happen without the ThreadLocal. Most likely you will get incorrect
|
||||
* date values and / or exceptions.
|
||||
* <p>
|
||||
* 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
|
||||
*
|
||||
* <p>This App shows how to create an isolated space per each thread. In this example the usage of
|
||||
* SimpleDateFormat is made to be thread-safe. This is an example of the ThreadLocal pattern.
|
||||
*
|
||||
* <p>By applying the ThreadLocal pattern you can keep track of application instances or locale
|
||||
* settings throughout the handling of a request. The ThreadLocal class works like a static
|
||||
* variable, with the exception that it is 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
|
||||
* holds a variable of type T, which is accessible via get/set methods.
|
||||
*
|
||||
* <p>SimpleDateFormat is one of the basic Java classes and is not thread-safe. If you do not
|
||||
* isolate the instance of SimpleDateFormat per each thread then problems arise.
|
||||
*
|
||||
* <p>App converts the String date value 15/12/2015 to the Date format using the Java class
|
||||
* SimpleDateFormat. It does this 20 times using 4 threads, each doing 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") and comment in the non ThreadLocal variant (marked with
|
||||
* "//NTLNTL") you can see what will happen without the ThreadLocal. Most likely you will get
|
||||
* incorrect date values and / or exceptions.
|
||||
*
|
||||
* <p>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 {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
|
||||
|
||||
/**
|
||||
* Program entry point
|
||||
*
|
||||
* @param args
|
||||
* command line args
|
||||
* Program entry point.
|
||||
*
|
||||
* @param args command line args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
int counterDateValues = 0;
|
||||
@ -115,9 +109,9 @@ public class App {
|
||||
}
|
||||
|
||||
/**
|
||||
* Print result (date values) of a thread execution and count dates
|
||||
*
|
||||
* @param res contains results of a thread execution
|
||||
* Print result (date values) of a thread execution and count dates.
|
||||
*
|
||||
* @param res contains results of a thread execution
|
||||
*/
|
||||
private static int printAndCountDates(Result res) {
|
||||
// a correct run should deliver 5 times 15.12.2015 per each thread
|
||||
@ -128,15 +122,16 @@ public class App {
|
||||
cal.setTime(dt);
|
||||
// Formatted output of the date value: DD.MM.YYYY
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print result (exceptions) of a thread execution and count exceptions
|
||||
*
|
||||
* @param res contains results of a thread execution
|
||||
* Print result (exceptions) of a thread execution and count exceptions.
|
||||
*
|
||||
* @param res contains results of a thread execution
|
||||
* @return number of dates
|
||||
*/
|
||||
private static int printAndCountExceptions(Result res) {
|
||||
|
@ -23,25 +23,23 @@
|
||||
|
||||
package com.iluwatar.tls;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.concurrent.Callable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* DateFormatCallable converts string dates to a date format using
|
||||
* SimpleDateFormat. The date format and the date value will be passed to the
|
||||
* Callable by the constructor. The constructor creates a instance of
|
||||
* SimpleDateFormat and stores it in a ThreadLocal class variable. For the
|
||||
* 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}
|
||||
* DateFormatCallable converts string dates to a date format using SimpleDateFormat. The date format
|
||||
* and the date value will be passed to the Callable by the constructor. The constructor creates a
|
||||
* instance of SimpleDateFormat and stores it in a ThreadLocal class variable. For the complete
|
||||
* description of the example see {@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> {
|
||||
|
||||
@ -51,15 +49,13 @@ public class DateFormatCallable implements Callable<Result> {
|
||||
// private DateFormat df; //NTLNTL
|
||||
|
||||
private String dateValue; // for dateValue Thread Local not needed
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The date format and the date value are passed to the constructor
|
||||
*
|
||||
* @param inDateFormat
|
||||
* string date format string, e.g. "dd/MM/yyyy"
|
||||
* @param inDateValue
|
||||
* string date value, e.g. "21/06/2016"
|
||||
* The date format and the date value are passed to the constructor.
|
||||
*
|
||||
* @param inDateFormat string date format string, e.g. "dd/MM/yyyy"
|
||||
* @param inDateValue string date value, e.g. "21/06/2016"
|
||||
*/
|
||||
public DateFormatCallable(String inDateFormat, String inDateValue) {
|
||||
final String idf = inDateFormat; //TLTL
|
||||
@ -73,9 +69,6 @@ public class DateFormatCallable implements Callable<Result> {
|
||||
this.dateValue = inDateValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.concurrent.Callable#call()
|
||||
*/
|
||||
@Override
|
||||
public Result call() {
|
||||
LOGGER.info(Thread.currentThread() + " started executing...");
|
||||
@ -88,7 +81,7 @@ public class DateFormatCallable implements Callable<Result> {
|
||||
// instance of SimpleDateFormat locally
|
||||
// Create the date value and store it in dateList
|
||||
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) {
|
||||
// write the Exception to a list and continue work
|
||||
result.getExceptionList().add(e.getClass() + ": " + e.getMessage());
|
||||
|
@ -32,10 +32,10 @@ import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Result object that will be returned by the Callable {@link DateFormatCallable}
|
||||
* used in {@link App}
|
||||
* Result object that will be returned by the Callable {@link DateFormatCallable} used in {@link
|
||||
* App}.
|
||||
*
|
||||
* @author Thomas Bauer, 2017
|
||||
* @author Thomas Bauer, 2017
|
||||
*/
|
||||
public class Result {
|
||||
// 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
|
||||
// this example)
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public List<String> getExceptionList() {
|
||||
|
@ -23,32 +23,29 @@
|
||||
|
||||
package com.iluwatar.tolerantreader;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
* 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
|
||||
* 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 {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
|
||||
|
||||
/**
|
||||
* Program entry point
|
||||
* Program entry point.
|
||||
*/
|
||||
public static void main(String[] args) throws IOException, ClassNotFoundException {
|
||||
// Write V1
|
||||
@ -59,8 +56,8 @@ public class App {
|
||||
// Read V1
|
||||
var deserializedRainbowFishV1 = RainbowFishSerializer.readV1("fish1.out");
|
||||
LOGGER.info("deserializedFishV1 name={} age={} length={} weight={}",
|
||||
deserializedRainbowFishV1.getName(), deserializedRainbowFishV1.getAge(),
|
||||
deserializedRainbowFishV1.getLengthMeters(), deserializedRainbowFishV1.getWeightTons());
|
||||
deserializedRainbowFishV1.getName(), deserializedRainbowFishV1.getAge(),
|
||||
deserializedRainbowFishV1.getLengthMeters(), deserializedRainbowFishV1.getWeightTons());
|
||||
// Write V2
|
||||
var fishV2 = new RainbowFishV2("Scar", 5, 12, 15, true, true, true);
|
||||
LOGGER.info(
|
||||
|
@ -26,9 +26,7 @@ package com.iluwatar.tolerantreader;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
*
|
||||
* RainbowFish is the initial schema
|
||||
*
|
||||
* RainbowFish is the initial schema.
|
||||
*/
|
||||
public class RainbowFish implements Serializable {
|
||||
|
||||
@ -40,7 +38,7 @@ public class RainbowFish implements Serializable {
|
||||
private int weightTons;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Constructor.
|
||||
*/
|
||||
public RainbowFish(String name, int age, int lengthMeters, int weightTons) {
|
||||
this.name = name;
|
||||
|
@ -28,16 +28,13 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* RainbowFishSerializer provides methods for reading and writing {@link RainbowFish} objects to
|
||||
* file. Tolerant Reader pattern is implemented here by serializing maps instead of
|
||||
* {@link RainbowFish} objects. This way the reader does not break even though new properties are
|
||||
* added to the schema.
|
||||
*
|
||||
* file. Tolerant Reader pattern is implemented here by serializing maps instead of {@link
|
||||
* RainbowFish} objects. This way the reader does not break even though new properties are added to
|
||||
* the schema.
|
||||
*/
|
||||
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 {
|
||||
var map = Map.of(
|
||||
"name", rainbowFish.getName(),
|
||||
"age", String.format("%d", rainbowFish.getAge()),
|
||||
"lengthMeters", String.format("%d", rainbowFish.getLengthMeters()),
|
||||
"weightTons", String.format("%d", rainbowFish.getWeightTons())
|
||||
"name", rainbowFish.getName(),
|
||||
"age", String.format("%d", rainbowFish.getAge()),
|
||||
"lengthMeters", String.format("%d", rainbowFish.getLengthMeters()),
|
||||
"weightTons", String.format("%d", rainbowFish.getWeightTons())
|
||||
);
|
||||
|
||||
try (var fileOut = new FileOutputStream(filename);
|
||||
var objOut = new ObjectOutputStream(fileOut)) {
|
||||
var objOut = new ObjectOutputStream(fileOut)) {
|
||||
objOut.writeObject(map);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write V2 RainbowFish to file
|
||||
* Write V2 RainbowFish to file.
|
||||
*/
|
||||
public static void writeV2(RainbowFishV2 rainbowFish, String filename) throws IOException {
|
||||
var map = Map.of(
|
||||
"name", rainbowFish.getName(),
|
||||
"age", String.format("%d", rainbowFish.getAge()),
|
||||
"lengthMeters", String.format("%d", rainbowFish.getLengthMeters()),
|
||||
"weightTons", String.format("%d", rainbowFish.getWeightTons()),
|
||||
"angry", Boolean.toString(rainbowFish.getAngry()),
|
||||
"hungry", Boolean.toString(rainbowFish.getHungry()),
|
||||
"sleeping", Boolean.toString(rainbowFish.getSleeping())
|
||||
"name", rainbowFish.getName(),
|
||||
"age", String.format("%d", rainbowFish.getAge()),
|
||||
"lengthMeters", String.format("%d", rainbowFish.getLengthMeters()),
|
||||
"weightTons", String.format("%d", rainbowFish.getWeightTons()),
|
||||
"angry", Boolean.toString(rainbowFish.getAngry()),
|
||||
"hungry", Boolean.toString(rainbowFish.getHungry()),
|
||||
"sleeping", Boolean.toString(rainbowFish.getSleeping())
|
||||
);
|
||||
|
||||
try (var fileOut = new FileOutputStream(filename);
|
||||
var objOut = new ObjectOutputStream(fileOut)) {
|
||||
var objOut = new ObjectOutputStream(fileOut)) {
|
||||
objOut.writeObject(map);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read V1 RainbowFish from file
|
||||
* Read V1 RainbowFish from file.
|
||||
*/
|
||||
public static RainbowFish readV1(String filename) throws IOException, ClassNotFoundException {
|
||||
Map<String, String> map = null;
|
||||
|
||||
try (var fileIn = new FileInputStream(filename);
|
||||
var objIn = new ObjectInputStream(fileIn)) {
|
||||
var objIn = new ObjectInputStream(fileIn)) {
|
||||
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")));
|
||||
}
|
||||
}
|
||||
|
@ -24,9 +24,7 @@
|
||||
package com.iluwatar.tolerantreader;
|
||||
|
||||
/**
|
||||
*
|
||||
* RainbowFishV2 is the evolved schema
|
||||
*
|
||||
* RainbowFishV2 is the evolved schema.
|
||||
*/
|
||||
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,
|
||||
boolean hungry, boolean angry) {
|
||||
boolean hungry, boolean angry) {
|
||||
this(name, age, lengthMeters, weightTons);
|
||||
this.sleeping = sleeping;
|
||||
this.hungry = hungry;
|
||||
|
Loading…
x
Reference in New Issue
Block a user