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;
|
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) {
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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.
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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());
|
||||||
|
@ -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() {
|
||||||
|
@ -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(
|
||||||
|
@ -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;
|
||||||
|
@ -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")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user