From 13a956c83839a4355a70388a424af5e625c51eb2 Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 14:03:30 +0000 Subject: [PATCH 1/9] add deadline --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d7a6ba3..e974d43 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/qcWcnElX) # Java concurrency # Цели и задачи л/р: From 5051aaf421770ad63ee058e875ca6014ef5a2dc7 Mon Sep 17 00:00:00 2001 From: Gogolev Andrey Date: Wed, 8 Oct 2025 21:54:32 +0300 Subject: [PATCH 2/9] base classes --- .../configuration/ProgrammerProperties.java | 37 +++++++++++ .../labs/configuration/WaiterProperties.java | 20 ++++++ src/main/java/org/labs/model/Plate.java | 36 +++++++++++ src/main/java/org/labs/model/PlateState.java | 7 +++ src/main/java/org/labs/model/Programmer.java | 63 +++++++++++++++++++ src/main/java/org/labs/model/Restaurant.java | 37 +++++++++++ src/main/java/org/labs/model/Spoon.java | 25 ++++++++ src/main/java/org/labs/model/Waiter.java | 40 ++++++++++++ 8 files changed, 265 insertions(+) create mode 100644 src/main/java/org/labs/configuration/ProgrammerProperties.java create mode 100644 src/main/java/org/labs/configuration/WaiterProperties.java create mode 100644 src/main/java/org/labs/model/Plate.java create mode 100644 src/main/java/org/labs/model/PlateState.java create mode 100644 src/main/java/org/labs/model/Programmer.java create mode 100644 src/main/java/org/labs/model/Restaurant.java create mode 100644 src/main/java/org/labs/model/Spoon.java create mode 100644 src/main/java/org/labs/model/Waiter.java diff --git a/src/main/java/org/labs/configuration/ProgrammerProperties.java b/src/main/java/org/labs/configuration/ProgrammerProperties.java new file mode 100644 index 0000000..ac8eef6 --- /dev/null +++ b/src/main/java/org/labs/configuration/ProgrammerProperties.java @@ -0,0 +1,37 @@ +package org.labs.configuration; + +import java.util.concurrent.ThreadLocalRandom; + +public class ProgrammerProperties { + private final int minEatingTime; + private final int maxEatingTime; + private final int thinkingTime; + private final int minChitChatTime; + private final int maxChitChatTime; + + public ProgrammerProperties( + int minEatingTime, + int maxEatingTime, + int thinkingTime, + int minChitChatTime, + int maxChitChatTime + ) { + this.minEatingTime = minEatingTime; + this.maxEatingTime = maxEatingTime; + this.thinkingTime = thinkingTime; + this.minChitChatTime = minChitChatTime; + this.maxChitChatTime = maxChitChatTime; + } + + public long getEatingTime() { + return ThreadLocalRandom.current().nextLong(minEatingTime, maxEatingTime); + } + + public long getThinkingTime() { + return thinkingTime; + } + + public long getChitChatTime() { + return ThreadLocalRandom.current().nextLong(minChitChatTime, maxChitChatTime); + } +} diff --git a/src/main/java/org/labs/configuration/WaiterProperties.java b/src/main/java/org/labs/configuration/WaiterProperties.java new file mode 100644 index 0000000..dfcfcc2 --- /dev/null +++ b/src/main/java/org/labs/configuration/WaiterProperties.java @@ -0,0 +1,20 @@ +package org.labs.configuration; + +import java.util.concurrent.ThreadLocalRandom; + +public class WaiterProperties { + private final int minServingTime; + private final int maxServingTime; + + public WaiterProperties( + int minServingTime, + int maxServingTime + ) { + this.minServingTime = minServingTime; + this.maxServingTime = maxServingTime; + } + + public long getServingTime() { + return ThreadLocalRandom.current().nextLong(minServingTime, maxServingTime); + } +} diff --git a/src/main/java/org/labs/model/Plate.java b/src/main/java/org/labs/model/Plate.java new file mode 100644 index 0000000..b3881e4 --- /dev/null +++ b/src/main/java/org/labs/model/Plate.java @@ -0,0 +1,36 @@ +package org.labs.model; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +public class Plate implements Comparable { + private final AtomicReference plateState; + private final AtomicInteger numOfRefills; + + public Plate() { + this.plateState = new AtomicReference<>(PlateState.EMPTY); + this.numOfRefills = new AtomicInteger(0); + } + + public PlateState getState() { + return plateState.get(); + } + + public void refill() { + plateState.set(PlateState.FULL); + numOfRefills.incrementAndGet(); + } + + public void finish() { + plateState.set(PlateState.EMPTY); + } + + public void order() { + plateState.set(PlateState.ORDERED); + } + + @Override + public int compareTo(Plate o) { + return Integer.compare(this.numOfRefills.get(), o.numOfRefills.get()); + } +} diff --git a/src/main/java/org/labs/model/PlateState.java b/src/main/java/org/labs/model/PlateState.java new file mode 100644 index 0000000..a2dfca2 --- /dev/null +++ b/src/main/java/org/labs/model/PlateState.java @@ -0,0 +1,7 @@ +package org.labs.model; + +public enum PlateState { + EMPTY, + ORDERED, + FULL +} diff --git a/src/main/java/org/labs/model/Programmer.java b/src/main/java/org/labs/model/Programmer.java new file mode 100644 index 0000000..12fc16f --- /dev/null +++ b/src/main/java/org/labs/model/Programmer.java @@ -0,0 +1,63 @@ +package org.labs.model; + +import org.labs.configuration.ProgrammerProperties; + +public class Programmer implements Runnable { + private final ProgrammerProperties properties; + private final Restaurant restaurant; + private final Plate plate; + private final Spoon smallerSpoon; + private final Spoon biggerSpoon; + + public Programmer( + ProgrammerProperties properties, + Restaurant restaurant, + Plate plate, + Spoon smallerSpoon, + Spoon biggerSpoon + ) { + this.properties = properties; + this.restaurant = restaurant; + this.plate = plate; + this.smallerSpoon = smallerSpoon; + this.biggerSpoon = biggerSpoon; + } + + @Override + public void run() { + try { + haveDinner(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + public void haveDinner() throws InterruptedException { + while (restaurant.isServing() || plate.getState() == PlateState.FULL) { + restaurant.makeOrder(plate); + + if (plate.getState() != PlateState.FULL) { + continue; + } + + eat(); + chitChat(); + } + } + + public void eat() throws InterruptedException { + try { + smallerSpoon.take(); + biggerSpoon.take(); + Thread.sleep(properties.getEatingTime()); + plate.finish(); + } finally { + biggerSpoon.put(); + smallerSpoon.put(); + } + } + + public void chitChat() throws InterruptedException { + Thread.sleep(properties.getChitChatTime()); + } +} diff --git a/src/main/java/org/labs/model/Restaurant.java b/src/main/java/org/labs/model/Restaurant.java new file mode 100644 index 0000000..e7299cb --- /dev/null +++ b/src/main/java/org/labs/model/Restaurant.java @@ -0,0 +1,37 @@ +package org.labs.model; + +import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.atomic.AtomicInteger; + +public class Restaurant { + private final PriorityBlockingQueue platesToServe; + private final AtomicInteger portionsLeft; + + public Restaurant(PriorityBlockingQueue platesToServe, int maxPortions) { + this.platesToServe = platesToServe; + this.portionsLeft = new AtomicInteger(maxPortions); + } + + public void makeOrder(Plate plate) { + if (plate.getState() == PlateState.EMPTY) { + plate.order(); + platesToServe.add(plate); + } + } + + public boolean hasPlates() { + return !platesToServe.isEmpty(); + } + + public Plate getPlate() { + return platesToServe.poll(); + } + + public boolean takeSoup() { + return portionsLeft.getAndUpdate(portionCount -> portionCount > 0 ? portionCount - 1 : 0) > 0; + } + + public boolean isServing() { + return portionsLeft.get() > 0; + } +} diff --git a/src/main/java/org/labs/model/Spoon.java b/src/main/java/org/labs/model/Spoon.java new file mode 100644 index 0000000..5791928 --- /dev/null +++ b/src/main/java/org/labs/model/Spoon.java @@ -0,0 +1,25 @@ +package org.labs.model; + +import java.util.concurrent.locks.ReentrantLock; + +public class Spoon { + private final int id; + private final ReentrantLock lock; + + public Spoon(int id) { + this.id = id; + this.lock = new ReentrantLock(); + } + + public int getId() { + return id; + } + + public void take() { + lock.lock(); + } + + public void put() { + lock.unlock(); + } +} diff --git a/src/main/java/org/labs/model/Waiter.java b/src/main/java/org/labs/model/Waiter.java new file mode 100644 index 0000000..8c90213 --- /dev/null +++ b/src/main/java/org/labs/model/Waiter.java @@ -0,0 +1,40 @@ +package org.labs.model; + +import org.labs.configuration.WaiterProperties; + +public class Waiter implements Runnable { + private final WaiterProperties properties; + private final Restaurant restaurant; + + public Waiter( + WaiterProperties properties, + Restaurant restaurant + ) { + this.properties = properties; + this.restaurant = restaurant; + } + + @Override + public void run() { + try { + serveProgrammers(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + public void serveProgrammers() throws InterruptedException { + while (restaurant.isServing() || restaurant.hasPlates()) { + Plate plate = restaurant.getPlate(); + if (plate == null) { + continue; + } + + Thread.sleep(properties.getServingTime()); + + if (restaurant.takeSoup()) { + plate.refill(); + } + } + } +} From 61caf75b4dff7a9d67ddbbab0c9ed94ea4ea4547 Mon Sep 17 00:00:00 2001 From: Gogolev Andrey Date: Wed, 8 Oct 2025 22:02:08 +0300 Subject: [PATCH 3/9] add logging --- build.gradle.kts | 2 ++ src/main/resources/logback.xml | 14 ++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 src/main/resources/logback.xml diff --git a/build.gradle.kts b/build.gradle.kts index bda0d97..e077d6f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,6 +12,8 @@ repositories { dependencies { testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation("org.junit.jupiter:junit-jupiter") + implementation("org.slf4j:slf4j-api:2.0.9") + implementation("ch.qos.logback:logback-classic:1.4.11") } tasks.test { diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..d965cd2 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,14 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + From a647587878a97dc46421d320241e1247c423f4e1 Mon Sep 17 00:00:00 2001 From: Gogolev Andrey Date: Wed, 8 Oct 2025 22:17:42 +0300 Subject: [PATCH 4/9] add logging --- src/main/java/org/labs/model/Plate.java | 12 +++++++++++- src/main/java/org/labs/model/Programmer.java | 20 +++++++++++++++++++- src/main/java/org/labs/model/Restaurant.java | 6 ++++++ src/main/java/org/labs/model/Waiter.java | 13 ++++++++++++- 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/labs/model/Plate.java b/src/main/java/org/labs/model/Plate.java index b3881e4..cada89d 100644 --- a/src/main/java/org/labs/model/Plate.java +++ b/src/main/java/org/labs/model/Plate.java @@ -6,8 +6,10 @@ public class Plate implements Comparable { private final AtomicReference plateState; private final AtomicInteger numOfRefills; + private final int programmerId; - public Plate() { + public Plate(int programmerId) { + this.programmerId = programmerId; this.plateState = new AtomicReference<>(PlateState.EMPTY); this.numOfRefills = new AtomicInteger(0); } @@ -16,6 +18,14 @@ public PlateState getState() { return plateState.get(); } + public int getNumOfRefills() { + return numOfRefills.get(); + } + + public int getProgrammerId() { + return programmerId; + } + public void refill() { plateState.set(PlateState.FULL); numOfRefills.incrementAndGet(); diff --git a/src/main/java/org/labs/model/Programmer.java b/src/main/java/org/labs/model/Programmer.java index 12fc16f..6e16b3f 100644 --- a/src/main/java/org/labs/model/Programmer.java +++ b/src/main/java/org/labs/model/Programmer.java @@ -1,26 +1,32 @@ package org.labs.model; import org.labs.configuration.ProgrammerProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Programmer implements Runnable { + private final Logger log = LoggerFactory.getLogger(Programmer.class); private final ProgrammerProperties properties; private final Restaurant restaurant; private final Plate plate; private final Spoon smallerSpoon; private final Spoon biggerSpoon; + private final Integer id; public Programmer( ProgrammerProperties properties, Restaurant restaurant, Plate plate, Spoon smallerSpoon, - Spoon biggerSpoon + Spoon biggerSpoon, + Integer id ) { this.properties = properties; this.restaurant = restaurant; this.plate = plate; this.smallerSpoon = smallerSpoon; this.biggerSpoon = biggerSpoon; + this.id = id; } @Override @@ -48,16 +54,28 @@ public void haveDinner() throws InterruptedException { public void eat() throws InterruptedException { try { smallerSpoon.take(); + log.info("Programmer with id [{}] acquired small spoon with id [{}]", id, smallerSpoon.getId()); + biggerSpoon.take(); + log.info("Programmer with id [{}] acquired bigger spoon with id [{}]", id, biggerSpoon.getId()); + Thread.sleep(properties.getEatingTime()); plate.finish(); + log.info("Programmer with id [{}] finished plate number [{}]", id, plate.getNumOfRefills()); } finally { biggerSpoon.put(); + log.info("Programmer with id [{}] put bigger spoon with id [{}]", id, biggerSpoon.getId()); + smallerSpoon.put(); + log.info("Programmer with id [{}] put smaller spoon with id [{}]", id, smallerSpoon.getId()); } } public void chitChat() throws InterruptedException { Thread.sleep(properties.getChitChatTime()); } + + public Plate getPlate() { + return plate; + } } diff --git a/src/main/java/org/labs/model/Restaurant.java b/src/main/java/org/labs/model/Restaurant.java index e7299cb..55fc0ed 100644 --- a/src/main/java/org/labs/model/Restaurant.java +++ b/src/main/java/org/labs/model/Restaurant.java @@ -3,7 +3,11 @@ import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.atomic.AtomicInteger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class Restaurant { + private final Logger log = LoggerFactory.getLogger(Restaurant.class); private final PriorityBlockingQueue platesToServe; private final AtomicInteger portionsLeft; @@ -15,6 +19,8 @@ public Restaurant(PriorityBlockingQueue platesToServe, int maxPortions) { public void makeOrder(Plate plate) { if (plate.getState() == PlateState.EMPTY) { plate.order(); + log.info("Programmer with id [{}] ordered new portion", plate.getProgrammerId()); + platesToServe.add(plate); } } diff --git a/src/main/java/org/labs/model/Waiter.java b/src/main/java/org/labs/model/Waiter.java index 8c90213..e6716cc 100644 --- a/src/main/java/org/labs/model/Waiter.java +++ b/src/main/java/org/labs/model/Waiter.java @@ -1,17 +1,23 @@ package org.labs.model; import org.labs.configuration.WaiterProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Waiter implements Runnable { + private final Logger log = LoggerFactory.getLogger(Waiter.class); private final WaiterProperties properties; private final Restaurant restaurant; + private final int id; public Waiter( WaiterProperties properties, - Restaurant restaurant + Restaurant restaurant, + int id ) { this.properties = properties; this.restaurant = restaurant; + this.id = id; } @Override @@ -34,6 +40,11 @@ public void serveProgrammers() throws InterruptedException { if (restaurant.takeSoup()) { plate.refill(); + log.info( + "Waiter with id [{}] refilled plate for programmer with id [{}]", + id, + plate.getProgrammerId() + ); } } } From 2d84a325484d8bc68755bcf3d9ba02144c6f0d63 Mon Sep 17 00:00:00 2001 From: Gogolev Andrey Date: Wed, 8 Oct 2025 22:53:04 +0300 Subject: [PATCH 5/9] add configuration and statistics logging --- build.gradle.kts | 5 + gradlew | 0 src/main/java/org/labs/Main.java | 128 +++++++++++++++++- .../org/labs/configuration/ConfigLoader.java | 33 +++++ .../configuration/ProgrammerProperties.java | 31 +---- .../labs/configuration/SimulationConfig.java | 10 ++ .../labs/configuration/WaiterProperties.java | 16 +-- .../labs/statistics/SimulationStatistics.java | 62 +++++++++ src/main/resources/simulation-config.json | 15 ++ 9 files changed, 262 insertions(+), 38 deletions(-) mode change 100644 => 100755 gradlew create mode 100644 src/main/java/org/labs/configuration/ConfigLoader.java create mode 100644 src/main/java/org/labs/configuration/SimulationConfig.java create mode 100644 src/main/java/org/labs/statistics/SimulationStatistics.java create mode 100644 src/main/resources/simulation-config.json diff --git a/build.gradle.kts b/build.gradle.kts index e077d6f..da10ffa 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,8 +12,13 @@ repositories { dependencies { testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation("org.junit.jupiter:junit-jupiter") + implementation("org.slf4j:slf4j-api:2.0.9") implementation("ch.qos.logback:logback-classic:1.4.11") + + implementation("com.fasterxml.jackson.core:jackson-databind:2.15.2") + implementation("com.fasterxml.jackson.core:jackson-core:2.15.2") + implementation("com.fasterxml.jackson.core:jackson-annotations:2.15.2") } tasks.test { diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/src/main/java/org/labs/Main.java b/src/main/java/org/labs/Main.java index 9917247..4b84050 100644 --- a/src/main/java/org/labs/Main.java +++ b/src/main/java/org/labs/Main.java @@ -1,7 +1,133 @@ package org.labs; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.TimeUnit; + +import org.labs.configuration.ConfigLoader; +import org.labs.configuration.SimulationConfig; +import org.labs.model.Plate; +import org.labs.model.Programmer; +import org.labs.model.Restaurant; +import org.labs.model.Spoon; +import org.labs.model.Waiter; +import org.labs.statistics.SimulationStatistics; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class Main { + private static final Logger logger = LoggerFactory.getLogger(Main.class); + public static void main(String[] args) { - System.out.println("Hello, World!"); + logger.info("Starting Dining Programmers Problem Simulation"); + + try { + SimulationConfig config = ConfigLoader.loadDefaultConfig(); + logger.info("Loaded configuration: {}", config); + + Simulation simulation = createSimulation(config); + + runSimulation(simulation, config); + } catch (Exception e) { + logger.error("Simulation failed", e); + System.exit(1); + } + } + + private static Simulation createSimulation(SimulationConfig config) { + PriorityBlockingQueue platesToServe = new PriorityBlockingQueue<>(); + + Restaurant restaurant = new Restaurant(platesToServe, config.totalFoodPortions()); + + List spoons = new ArrayList<>(); + for (int i = 0; i < config.numberOfProgrammers(); i++) { + spoons.add(new Spoon(i)); + } + + List plates = new ArrayList<>(); + List programmers = new ArrayList<>(); + + for (int i = 0; i < config.numberOfProgrammers(); i++) { + Plate plate = new Plate(i); + plates.add(plate); + + Spoon leftSpoon = spoons.get(i); + Spoon rightSpoon = spoons.get((i + 1) % config.numberOfProgrammers()); + Spoon smallerSpoon = leftSpoon.getId() < rightSpoon.getId() ? leftSpoon : rightSpoon; + Spoon biggerSpoon = leftSpoon.getId() > rightSpoon.getId() ? leftSpoon : rightSpoon; + + Programmer programmer = new Programmer( + config.programmerProperties(), + restaurant, + plate, + smallerSpoon, + biggerSpoon, + i + ); + programmers.add(programmer); + } + + List waiters = new ArrayList<>(); + for (int i = 0; i < config.numberOfWaiters(); i++) { + Waiter waiter = new Waiter(config.waiterProperties(), restaurant, i); + waiters.add(waiter); + } + + return new Simulation(restaurant, programmers, waiters, plates); + } + + private static void runSimulation(Simulation simulation, SimulationConfig config) throws InterruptedException { + try ( + ExecutorService programmerPool = Executors.newVirtualThreadPerTaskExecutor(); + ExecutorService waiterPool = Executors.newVirtualThreadPerTaskExecutor(); + ) { + SimulationStatistics statistics = new SimulationStatistics(); + + logger.info( + "Starting simulation with [{}] programmers and [{}] waiters", + config.numberOfProgrammers(), + config.numberOfWaiters() + ); + + try { + for (Programmer programmer : simulation.programmers) { + programmerPool.submit(programmer); + } + for (Waiter waiter : simulation.waiters) { + waiterPool.submit(waiter); + } + } finally { + programmerPool.shutdown(); + waiterPool.shutdown(); + + while (!programmerPool.awaitTermination(5, TimeUnit.SECONDS)) { + logger.warn("Waiting for programmers pool to terminate"); + } + + while (!waiterPool.awaitTermination(5, TimeUnit.SECONDS)) { + logger.warn("Waiting for waiters pool to terminate"); + } + } + + statistics.printFinalStatistics(simulation.plates); + + int totalRefills = simulation.plates.stream() + .mapToInt(Plate::getNumOfRefills) + .sum(); + + logger.info("Total refills from plates: {}", totalRefills); + logger.info("Simulation completed successfully!"); + } + } + + private record Simulation( + Restaurant restaurant, + List programmers, + List waiters, + List plates + ) { } } \ No newline at end of file diff --git a/src/main/java/org/labs/configuration/ConfigLoader.java b/src/main/java/org/labs/configuration/ConfigLoader.java new file mode 100644 index 0000000..54077e1 --- /dev/null +++ b/src/main/java/org/labs/configuration/ConfigLoader.java @@ -0,0 +1,33 @@ +package org.labs.configuration; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; + +public class ConfigLoader { + private static final Logger logger = LoggerFactory.getLogger(ConfigLoader.class); + private static final ObjectMapper objectMapper = new ObjectMapper(); + + public static SimulationConfig loadConfig(String configPath) { + try (InputStream inputStream = ConfigLoader.class.getClassLoader().getResourceAsStream(configPath)) { + if (inputStream == null) { + throw new RuntimeException("Configuration file not found: " + configPath); + } + + SimulationConfig config = objectMapper.readValue(inputStream, SimulationConfig.class); + logger.info("Loaded simulation configuration: {}", config); + return config; + + } catch (IOException e) { + logger.error("Failed to load configuration from {}", configPath, e); + throw new RuntimeException("Failed to load configuration", e); + } + } + + public static SimulationConfig loadDefaultConfig() { + return loadConfig("simulation-config.json"); + } +} diff --git a/src/main/java/org/labs/configuration/ProgrammerProperties.java b/src/main/java/org/labs/configuration/ProgrammerProperties.java index ac8eef6..109f4e9 100644 --- a/src/main/java/org/labs/configuration/ProgrammerProperties.java +++ b/src/main/java/org/labs/configuration/ProgrammerProperties.java @@ -2,35 +2,16 @@ import java.util.concurrent.ThreadLocalRandom; -public class ProgrammerProperties { - private final int minEatingTime; - private final int maxEatingTime; - private final int thinkingTime; - private final int minChitChatTime; - private final int maxChitChatTime; - - public ProgrammerProperties( - int minEatingTime, - int maxEatingTime, - int thinkingTime, - int minChitChatTime, - int maxChitChatTime - ) { - this.minEatingTime = minEatingTime; - this.maxEatingTime = maxEatingTime; - this.thinkingTime = thinkingTime; - this.minChitChatTime = minChitChatTime; - this.maxChitChatTime = maxChitChatTime; - } - +public record ProgrammerProperties( + int minEatingTime, + int maxEatingTime, + int minChitChatTime, + int maxChitChatTime +) { public long getEatingTime() { return ThreadLocalRandom.current().nextLong(minEatingTime, maxEatingTime); } - public long getThinkingTime() { - return thinkingTime; - } - public long getChitChatTime() { return ThreadLocalRandom.current().nextLong(minChitChatTime, maxChitChatTime); } diff --git a/src/main/java/org/labs/configuration/SimulationConfig.java b/src/main/java/org/labs/configuration/SimulationConfig.java new file mode 100644 index 0000000..034d8b7 --- /dev/null +++ b/src/main/java/org/labs/configuration/SimulationConfig.java @@ -0,0 +1,10 @@ +package org.labs.configuration; + +public record SimulationConfig( + int numberOfProgrammers, + int numberOfWaiters, + int totalFoodPortions, + ProgrammerProperties programmerProperties, + WaiterProperties waiterProperties +) { +} diff --git a/src/main/java/org/labs/configuration/WaiterProperties.java b/src/main/java/org/labs/configuration/WaiterProperties.java index dfcfcc2..7395fdd 100644 --- a/src/main/java/org/labs/configuration/WaiterProperties.java +++ b/src/main/java/org/labs/configuration/WaiterProperties.java @@ -2,18 +2,10 @@ import java.util.concurrent.ThreadLocalRandom; -public class WaiterProperties { - private final int minServingTime; - private final int maxServingTime; - - public WaiterProperties( - int minServingTime, - int maxServingTime - ) { - this.minServingTime = minServingTime; - this.maxServingTime = maxServingTime; - } - +public record WaiterProperties( + int minServingTime, + int maxServingTime +) { public long getServingTime() { return ThreadLocalRandom.current().nextLong(minServingTime, maxServingTime); } diff --git a/src/main/java/org/labs/statistics/SimulationStatistics.java b/src/main/java/org/labs/statistics/SimulationStatistics.java new file mode 100644 index 0000000..5641189 --- /dev/null +++ b/src/main/java/org/labs/statistics/SimulationStatistics.java @@ -0,0 +1,62 @@ +package org.labs.statistics; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.labs.model.Plate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SimulationStatistics { + private static final Logger logger = LoggerFactory.getLogger(SimulationStatistics.class); + + private final Map programmerPortions = new HashMap<>(); + private final long startTime; + + public SimulationStatistics() { + this.startTime = System.currentTimeMillis(); + } + + public void printFinalStatistics(List plates) { + long endTime = System.currentTimeMillis(); + long totalTime = endTime - startTime; + + logger.info("=== SIMULATION STATISTICS ==="); + logger.info("Total simulation time: {} ms ({} seconds)", totalTime, totalTime / 1000.0); + + int totalRefills = plates.stream() + .mapToInt(Plate::getNumOfRefills) + .sum(); + logger.info("Total plate refills: {}", totalRefills); + + logger.info("=== PROGRAMMER STATISTICS ==="); + plates.stream() + .sorted(Comparator.comparing(Plate::getProgrammerId)) + .forEach(plate -> { + int programmerId = plate.getProgrammerId(); + int portionsEaten = plate.getNumOfRefills(); + logger.info("Programmer {} ate {} portions", programmerId, portionsEaten); + programmerPortions.put(programmerId, portionsEaten); + }); + + int minPortions = programmerPortions.values().stream() + .min(Integer::compare) + .orElse(0); + int maxPortions = programmerPortions.values().stream() + .max(Integer::compare) + .orElse(0); + + int difference = maxPortions - minPortions; + + logger.info("=== FAIRNESS ANALYSIS ==="); + logger.info("Min portions eaten: {}", minPortions); + logger.info("Max portions eaten: {}", maxPortions); + logger.info("Difference: {} portions", difference); + logger.info( + "Fairness ratio: {:.2f}%", + minPortions > 0 ? (double) minPortions / maxPortions * 100 : 0 + ); + } +} diff --git a/src/main/resources/simulation-config.json b/src/main/resources/simulation-config.json new file mode 100644 index 0000000..70a2842 --- /dev/null +++ b/src/main/resources/simulation-config.json @@ -0,0 +1,15 @@ +{ + "numberOfProgrammers": 7, + "numberOfWaiters": 2, + "totalFoodPortions": 1000000, + "programmerProperties": { + "minEatingTime": 200, + "maxEatingTime": 250, + "minChitChatTime": 50, + "maxChitChatTime": 60 + }, + "waiterProperties": { + "minServingTime": 200, + "maxServingTime": 250 + } +} From b202cc8fb89c979b52f2460b2a9d13f88fd4877a Mon Sep 17 00:00:00 2001 From: Gogolev Andrey Date: Wed, 8 Oct 2025 23:12:09 +0300 Subject: [PATCH 6/9] refactor --- src/main/java/org/labs/Main.java | 113 +----------------- .../java/org/labs/simulation/Simulation.java | 16 +++ .../org/labs/simulation/SimulationUtils.java | 104 ++++++++++++++++ .../labs/statistics/SimulationStatistics.java | 1 + 4 files changed, 127 insertions(+), 107 deletions(-) create mode 100644 src/main/java/org/labs/simulation/Simulation.java create mode 100644 src/main/java/org/labs/simulation/SimulationUtils.java diff --git a/src/main/java/org/labs/Main.java b/src/main/java/org/labs/Main.java index 4b84050..b8a4ba9 100644 --- a/src/main/java/org/labs/Main.java +++ b/src/main/java/org/labs/Main.java @@ -1,23 +1,15 @@ package org.labs; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.PriorityBlockingQueue; -import java.util.concurrent.TimeUnit; - import org.labs.configuration.ConfigLoader; import org.labs.configuration.SimulationConfig; -import org.labs.model.Plate; -import org.labs.model.Programmer; -import org.labs.model.Restaurant; -import org.labs.model.Spoon; -import org.labs.model.Waiter; +import org.labs.simulation.Simulation; import org.labs.statistics.SimulationStatistics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.labs.simulation.SimulationUtils.createSimulation; +import static org.labs.simulation.SimulationUtils.runSimulation; + public class Main { private static final Logger logger = LoggerFactory.getLogger(Main.class); @@ -30,104 +22,11 @@ public static void main(String[] args) { Simulation simulation = createSimulation(config); - runSimulation(simulation, config); + SimulationStatistics statistics = runSimulation(simulation, config); + statistics.printFinalStatistics(simulation.plates()); } catch (Exception e) { logger.error("Simulation failed", e); System.exit(1); } } - - private static Simulation createSimulation(SimulationConfig config) { - PriorityBlockingQueue platesToServe = new PriorityBlockingQueue<>(); - - Restaurant restaurant = new Restaurant(platesToServe, config.totalFoodPortions()); - - List spoons = new ArrayList<>(); - for (int i = 0; i < config.numberOfProgrammers(); i++) { - spoons.add(new Spoon(i)); - } - - List plates = new ArrayList<>(); - List programmers = new ArrayList<>(); - - for (int i = 0; i < config.numberOfProgrammers(); i++) { - Plate plate = new Plate(i); - plates.add(plate); - - Spoon leftSpoon = spoons.get(i); - Spoon rightSpoon = spoons.get((i + 1) % config.numberOfProgrammers()); - Spoon smallerSpoon = leftSpoon.getId() < rightSpoon.getId() ? leftSpoon : rightSpoon; - Spoon biggerSpoon = leftSpoon.getId() > rightSpoon.getId() ? leftSpoon : rightSpoon; - - Programmer programmer = new Programmer( - config.programmerProperties(), - restaurant, - plate, - smallerSpoon, - biggerSpoon, - i - ); - programmers.add(programmer); - } - - List waiters = new ArrayList<>(); - for (int i = 0; i < config.numberOfWaiters(); i++) { - Waiter waiter = new Waiter(config.waiterProperties(), restaurant, i); - waiters.add(waiter); - } - - return new Simulation(restaurant, programmers, waiters, plates); - } - - private static void runSimulation(Simulation simulation, SimulationConfig config) throws InterruptedException { - try ( - ExecutorService programmerPool = Executors.newVirtualThreadPerTaskExecutor(); - ExecutorService waiterPool = Executors.newVirtualThreadPerTaskExecutor(); - ) { - SimulationStatistics statistics = new SimulationStatistics(); - - logger.info( - "Starting simulation with [{}] programmers and [{}] waiters", - config.numberOfProgrammers(), - config.numberOfWaiters() - ); - - try { - for (Programmer programmer : simulation.programmers) { - programmerPool.submit(programmer); - } - for (Waiter waiter : simulation.waiters) { - waiterPool.submit(waiter); - } - } finally { - programmerPool.shutdown(); - waiterPool.shutdown(); - - while (!programmerPool.awaitTermination(5, TimeUnit.SECONDS)) { - logger.warn("Waiting for programmers pool to terminate"); - } - - while (!waiterPool.awaitTermination(5, TimeUnit.SECONDS)) { - logger.warn("Waiting for waiters pool to terminate"); - } - } - - statistics.printFinalStatistics(simulation.plates); - - int totalRefills = simulation.plates.stream() - .mapToInt(Plate::getNumOfRefills) - .sum(); - - logger.info("Total refills from plates: {}", totalRefills); - logger.info("Simulation completed successfully!"); - } - } - - private record Simulation( - Restaurant restaurant, - List programmers, - List waiters, - List plates - ) { - } } \ No newline at end of file diff --git a/src/main/java/org/labs/simulation/Simulation.java b/src/main/java/org/labs/simulation/Simulation.java new file mode 100644 index 0000000..b222e89 --- /dev/null +++ b/src/main/java/org/labs/simulation/Simulation.java @@ -0,0 +1,16 @@ +package org.labs.simulation; + +import java.util.List; + +import org.labs.model.Plate; +import org.labs.model.Programmer; +import org.labs.model.Restaurant; +import org.labs.model.Waiter; + +public record Simulation( + Restaurant restaurant, + List programmers, + List waiters, + List plates +) { +} \ No newline at end of file diff --git a/src/main/java/org/labs/simulation/SimulationUtils.java b/src/main/java/org/labs/simulation/SimulationUtils.java new file mode 100644 index 0000000..a83dd66 --- /dev/null +++ b/src/main/java/org/labs/simulation/SimulationUtils.java @@ -0,0 +1,104 @@ +package org.labs.simulation; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.TimeUnit; + +import org.labs.configuration.SimulationConfig; +import org.labs.model.Plate; +import org.labs.model.Programmer; +import org.labs.model.Restaurant; +import org.labs.model.Spoon; +import org.labs.model.Waiter; +import org.labs.statistics.SimulationStatistics; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SimulationUtils { + private final static Logger log = LoggerFactory.getLogger(SimulationUtils.class); + + public static Simulation createSimulation(SimulationConfig config) { + PriorityBlockingQueue platesToServe = new PriorityBlockingQueue<>(); + + Restaurant restaurant = new Restaurant(platesToServe, config.totalFoodPortions()); + + List spoons = new ArrayList<>(); + for (int i = 0; i < config.numberOfProgrammers(); i++) { + spoons.add(new Spoon(i)); + } + + List plates = new ArrayList<>(); + List programmers = new ArrayList<>(); + + for (int i = 0; i < config.numberOfProgrammers(); i++) { + Plate plate = new Plate(i); + plates.add(plate); + + Spoon leftSpoon = spoons.get(i); + Spoon rightSpoon = spoons.get((i + 1) % config.numberOfProgrammers()); + Spoon smallerSpoon = leftSpoon.getId() < rightSpoon.getId() ? leftSpoon : rightSpoon; + Spoon biggerSpoon = leftSpoon.getId() > rightSpoon.getId() ? leftSpoon : rightSpoon; + + Programmer programmer = new Programmer( + config.programmerProperties(), + restaurant, + plate, + smallerSpoon, + biggerSpoon, + i + ); + programmers.add(programmer); + } + + List waiters = new ArrayList<>(); + for (int i = 0; i < config.numberOfWaiters(); i++) { + Waiter waiter = new Waiter(config.waiterProperties(), restaurant, i); + waiters.add(waiter); + } + + return new Simulation(restaurant, programmers, waiters, plates); + } + + public static SimulationStatistics runSimulation( + Simulation simulation, + SimulationConfig config + ) throws InterruptedException { + try ( + ExecutorService programmerPool = Executors.newVirtualThreadPerTaskExecutor(); + ExecutorService waiterPool = Executors.newVirtualThreadPerTaskExecutor(); + ) { + SimulationStatistics statistics = new SimulationStatistics(); + + log.info( + "Starting simulation with [{}] programmers and [{}] waiters", + config.numberOfProgrammers(), + config.numberOfWaiters() + ); + + try { + for (Programmer programmer : simulation.programmers()) { + programmerPool.submit(programmer); + } + for (Waiter waiter : simulation.waiters()) { + waiterPool.submit(waiter); + } + } finally { + programmerPool.shutdown(); + waiterPool.shutdown(); + + while (!programmerPool.awaitTermination(5, TimeUnit.SECONDS)) { + log.warn("Waiting for programmers pool to terminate"); + } + + while (!waiterPool.awaitTermination(5, TimeUnit.SECONDS)) { + log.warn("Waiting for waiters pool to terminate"); + } + } + + return statistics; + } + } +} diff --git a/src/main/java/org/labs/statistics/SimulationStatistics.java b/src/main/java/org/labs/statistics/SimulationStatistics.java index 5641189..48ebea3 100644 --- a/src/main/java/org/labs/statistics/SimulationStatistics.java +++ b/src/main/java/org/labs/statistics/SimulationStatistics.java @@ -51,6 +51,7 @@ public void printFinalStatistics(List plates) { int difference = maxPortions - minPortions; logger.info("=== FAIRNESS ANALYSIS ==="); + logger.info("Total refills from plates: {}", totalRefills); logger.info("Min portions eaten: {}", minPortions); logger.info("Max portions eaten: {}", maxPortions); logger.info("Difference: {} portions", difference); From ca206fe7431f48e0ca771a7d594f783bd0029065 Mon Sep 17 00:00:00 2001 From: Gogolev Andrey Date: Wed, 8 Oct 2025 23:31:38 +0300 Subject: [PATCH 7/9] refactor --- .../org/labs/statistics/SimulationStatistics.java | 2 +- src/main/resources/simulation-config.json | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/labs/statistics/SimulationStatistics.java b/src/main/java/org/labs/statistics/SimulationStatistics.java index 48ebea3..3c84950 100644 --- a/src/main/java/org/labs/statistics/SimulationStatistics.java +++ b/src/main/java/org/labs/statistics/SimulationStatistics.java @@ -56,7 +56,7 @@ public void printFinalStatistics(List plates) { logger.info("Max portions eaten: {}", maxPortions); logger.info("Difference: {} portions", difference); logger.info( - "Fairness ratio: {:.2f}%", + "Fairness ratio: {}%", minPortions > 0 ? (double) minPortions / maxPortions * 100 : 0 ); } diff --git a/src/main/resources/simulation-config.json b/src/main/resources/simulation-config.json index 70a2842..8c82f5d 100644 --- a/src/main/resources/simulation-config.json +++ b/src/main/resources/simulation-config.json @@ -3,13 +3,13 @@ "numberOfWaiters": 2, "totalFoodPortions": 1000000, "programmerProperties": { - "minEatingTime": 200, - "maxEatingTime": 250, - "minChitChatTime": 50, - "maxChitChatTime": 60 + "minEatingTime": 1, + "maxEatingTime": 2, + "minChitChatTime": 3, + "maxChitChatTime": 4 }, "waiterProperties": { - "minServingTime": 200, - "maxServingTime": 250 + "minServingTime": 1, + "maxServingTime": 2 } } From edc60915ddfedaa379c02f4218f92cb56de6a595 Mon Sep 17 00:00:00 2001 From: Gogolev Andrey Date: Thu, 9 Oct 2025 18:15:52 +0300 Subject: [PATCH 8/9] refactor statistics, add test --- .../org/labs/simulation/SimulationUtils.java | 6 +- .../labs/statistics/SimulationStatistics.java | 69 ++++++++++--------- src/test/java/org/labs/SimulationTest.java | 43 ++++++++++++ src/test/resources/test-config.json | 15 ++++ 4 files changed, 100 insertions(+), 33 deletions(-) create mode 100644 src/test/java/org/labs/SimulationTest.java create mode 100644 src/test/resources/test-config.json diff --git a/src/main/java/org/labs/simulation/SimulationUtils.java b/src/main/java/org/labs/simulation/SimulationUtils.java index a83dd66..b96c830 100644 --- a/src/main/java/org/labs/simulation/SimulationUtils.java +++ b/src/main/java/org/labs/simulation/SimulationUtils.java @@ -69,8 +69,10 @@ public static SimulationStatistics runSimulation( try ( ExecutorService programmerPool = Executors.newVirtualThreadPerTaskExecutor(); ExecutorService waiterPool = Executors.newVirtualThreadPerTaskExecutor(); +// ExecutorService programmerPool = Executors.newFixedThreadPool(config.numberOfProgrammers()); +// ExecutorService waiterPool = Executors.newFixedThreadPool(config.numberOfProgrammers()) ) { - SimulationStatistics statistics = new SimulationStatistics(); + long startTime = System.currentTimeMillis(); log.info( "Starting simulation with [{}] programmers and [{}] waiters", @@ -98,7 +100,7 @@ public static SimulationStatistics runSimulation( } } - return statistics; + return new SimulationStatistics(startTime, simulation.plates()); } } } diff --git a/src/main/java/org/labs/statistics/SimulationStatistics.java b/src/main/java/org/labs/statistics/SimulationStatistics.java index 3c84950..798a759 100644 --- a/src/main/java/org/labs/statistics/SimulationStatistics.java +++ b/src/main/java/org/labs/statistics/SimulationStatistics.java @@ -1,9 +1,8 @@ package org.labs.statistics; -import java.util.Comparator; -import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import org.labs.model.Plate; import org.slf4j.Logger; @@ -12,52 +11,60 @@ public class SimulationStatistics { private static final Logger logger = LoggerFactory.getLogger(SimulationStatistics.class); - private final Map programmerPortions = new HashMap<>(); - private final long startTime; + private final Map programmerPortions; + private final long totalTime; - public SimulationStatistics() { - this.startTime = System.currentTimeMillis(); - } - - public void printFinalStatistics(List plates) { - long endTime = System.currentTimeMillis(); - long totalTime = endTime - startTime; - - logger.info("=== SIMULATION STATISTICS ==="); - logger.info("Total simulation time: {} ms ({} seconds)", totalTime, totalTime / 1000.0); + private final int totalRefills; + private final int minPortions; + private final int maxPortions; + private final double fairnessRatio; - int totalRefills = plates.stream() + public SimulationStatistics(Long startTime, List plates) { + this.totalTime = System.currentTimeMillis() - startTime; + this.totalRefills = plates.stream() .mapToInt(Plate::getNumOfRefills) .sum(); - logger.info("Total plate refills: {}", totalRefills); - logger.info("=== PROGRAMMER STATISTICS ==="); - plates.stream() - .sorted(Comparator.comparing(Plate::getProgrammerId)) - .forEach(plate -> { - int programmerId = plate.getProgrammerId(); - int portionsEaten = plate.getNumOfRefills(); - logger.info("Programmer {} ate {} portions", programmerId, portionsEaten); - programmerPortions.put(programmerId, portionsEaten); - }); - - int minPortions = programmerPortions.values().stream() + this.programmerPortions = plates.stream() + .collect(Collectors.toMap( + Plate::getProgrammerId, + Plate::getNumOfRefills + )); + this.minPortions = programmerPortions.values().stream() .min(Integer::compare) .orElse(0); - int maxPortions = programmerPortions.values().stream() + this.maxPortions = programmerPortions.values().stream() .max(Integer::compare) .orElse(0); - int difference = maxPortions - minPortions; + this.fairnessRatio = (double) minPortions / maxPortions * 100; + } + + public void printFinalStatistics(List plates) { + logger.info("=== SIMULATION STATISTICS ==="); + logger.info("Total simulation time: {} ms ({} seconds)", totalTime, totalTime / 1000.0); + logger.info("Total plate refills: {}", totalRefills); + + logger.info("=== PROGRAMMER STATISTICS ==="); + + programmerPortions.forEach((key, value) -> logger.info("Programmer {} ate {} portions", key, value)); logger.info("=== FAIRNESS ANALYSIS ==="); logger.info("Total refills from plates: {}", totalRefills); logger.info("Min portions eaten: {}", minPortions); logger.info("Max portions eaten: {}", maxPortions); - logger.info("Difference: {} portions", difference); + logger.info("Difference: {} portions", maxPortions - minPortions); logger.info( "Fairness ratio: {}%", - minPortions > 0 ? (double) minPortions / maxPortions * 100 : 0 + minPortions > 0 ? fairnessRatio : 0 ); } + + public int getTotalRefills() { + return totalRefills; + } + + public double getFairnessRation() { + return fairnessRatio; + } } diff --git a/src/test/java/org/labs/SimulationTest.java b/src/test/java/org/labs/SimulationTest.java new file mode 100644 index 0000000..0e6ebe8 --- /dev/null +++ b/src/test/java/org/labs/SimulationTest.java @@ -0,0 +1,43 @@ +package org.labs; + +import org.junit.jupiter.api.Test; +import org.labs.configuration.ConfigLoader; +import org.labs.configuration.SimulationConfig; +import org.labs.model.Plate; +import org.labs.simulation.Simulation; +import org.labs.statistics.SimulationStatistics; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.labs.simulation.SimulationUtils.createSimulation; +import static org.labs.simulation.SimulationUtils.runSimulation; + +class SimulationTest { + + @Test + void testTotalPortionsEaten() throws Exception { + SimulationConfig config = ConfigLoader.loadConfig("test-config.json"); + + Simulation simulation = createSimulation(config); + + SimulationStatistics statistics = runSimulation(simulation, config); + + assertEquals(config.totalFoodPortions(), statistics.getTotalRefills(), + "Total refills should equal total food portions"); + + for (Plate plate : simulation.plates()) { + assertTrue(plate.getNumOfRefills() > 0, + "Each programmer should have eaten at least one portion"); + } + + + assertTrue( + statistics.getFairnessRation() >= 0.95d, + String.format( + "Fairness check failed: fairness ratio should be >= 0.95, but was {%s}", + statistics.getFairnessRation() + ) + ); + } +} + diff --git a/src/test/resources/test-config.json b/src/test/resources/test-config.json new file mode 100644 index 0000000..31d7a8a --- /dev/null +++ b/src/test/resources/test-config.json @@ -0,0 +1,15 @@ +{ + "numberOfProgrammers": 7, + "numberOfWaiters": 2, + "totalFoodPortions": 100, + "programmerProperties": { + "minEatingTime": 5, + "maxEatingTime": 10, + "minChitChatTime": 7, + "maxChitChatTime": 9 + }, + "waiterProperties": { + "minServingTime": 5, + "maxServingTime": 10 + } +} \ No newline at end of file From ef2fcda7e07065e2c611fbde8c44c8ef33459db4 Mon Sep 17 00:00:00 2001 From: Gogolev Andrey Date: Thu, 9 Oct 2025 18:23:54 +0300 Subject: [PATCH 9/9] add file appender for logs --- src/main/resources/logback.xml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index d965cd2..ee88e97 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -7,6 +7,32 @@ + + logs/simulation.log + + + logs/simulation.%d{yyyy-MM-dd}.%i.log + 10MB + 30 + 1GB + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + 1024 + 0 + false + + + + + +