diff --git a/.gitignore b/.gitignore index 3ff9c9c..3e001ad 100644 --- a/.gitignore +++ b/.gitignore @@ -50,4 +50,5 @@ bin/ .vscode/ ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store +CLAUDE.md \ No newline at end of file diff --git a/Task_2_2_1/.gitattributes b/Task_2_2_1/.gitattributes new file mode 100644 index 0000000..f91f646 --- /dev/null +++ b/Task_2_2_1/.gitattributes @@ -0,0 +1,12 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# Linux start script should use lf +/gradlew text eol=lf + +# These are Windows script files and should use crlf +*.bat text eol=crlf + +# Binary files should be left untouched +*.jar binary + diff --git a/Task_2_2_1/.gitignore b/Task_2_2_1/.gitignore new file mode 100644 index 0000000..1b6985c --- /dev/null +++ b/Task_2_2_1/.gitignore @@ -0,0 +1,5 @@ +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +build diff --git a/Task_2_2_1/app/build.gradle b/Task_2_2_1/app/build.gradle new file mode 100644 index 0000000..efe7b6b --- /dev/null +++ b/Task_2_2_1/app/build.gradle @@ -0,0 +1,58 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Java application project to get you started. + * For more details on building Java & JVM projects, please refer to https://docs.gradle.org/9.0.0/userguide/building_java_projects.html in the Gradle documentation. + */ + +plugins { + // Apply the application plugin to add support for building a CLI application in Java. + id 'application' + id 'jacoco' +} + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() +} + +dependencies { + // Use JUnit Jupiter for testing. + testImplementation libs.junit.jupiter + + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + // This dependency is used by the application. + implementation libs.guava + implementation 'com.google.code.gson:gson:2.11.0' +} + +// Apply a specific Java toolchain to ease working on different environments. +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + +application { + // Define the main class for the application. + mainClass = 'org.example.Main' +} + +tasks.named('test') { + // Use JUnit Platform for unit tests. + useJUnitPlatform() +} + +apply plugin: 'jacoco' + + +jacocoTestReport { + + dependsOn test + reports { + xml.required = true + + + } +} diff --git a/Task_2_2_1/app/src/main/java/org/example/Main.java b/Task_2_2_1/app/src/main/java/org/example/Main.java new file mode 100644 index 0000000..1eab13e --- /dev/null +++ b/Task_2_2_1/app/src/main/java/org/example/Main.java @@ -0,0 +1,48 @@ +package org.example; + +import org.example.config.PizzeriaConfig; +import org.example.exception.ConfigLoadException; +import org.example.exception.PizzeriaInterruptException; + +/** + * Main. + */ +public class Main { + /** + * Запуск. + * + * @param args аргументы + */ + public static void main(String[] args) { + Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> { + switch (ex) { + case ConfigLoadException e -> + System.err.println("Ошибка конфигурации " + e.getMessage()); + case PizzeriaInterruptException e -> + System.err.println(thread.getName() + " Прерывание " + e.getMessage()); + default -> + System.err.println(thread.getName() + " Ошибка " + ex.getMessage()); + } + }); + + try { + PizzeriaConfig config = PizzeriaConfig.load("/config.json"); + System.out.println("Пиццерия открывается!"); + System.out.println("Пекарей: " + config.bakers.length); + System.out.println("Курьеров: " + config.couriers.length); + System.out.println("Вместимость склада: " + config.storageCapacity); + System.out.println("Время работы: " + config.workingTimeMs + " мс"); + System.out.println("Заказов: " + config.orders.length); + System.out.println(); + + Pizzeria pizzeria = new Pizzeria(config); + pizzeria.start(); + } catch (ConfigLoadException e) { + System.err.println("Ошибка конфигурации " + e.getMessage()); + } catch (PizzeriaInterruptException e) { + System.err.println("Прерывание работы " + e.getMessage()); + } catch (Exception e) { + System.err.println("Какая-то ошибка " + e.getMessage()); + } + } +} diff --git a/Task_2_2_1/app/src/main/java/org/example/Pizzeria.java b/Task_2_2_1/app/src/main/java/org/example/Pizzeria.java new file mode 100644 index 0000000..a43e33b --- /dev/null +++ b/Task_2_2_1/app/src/main/java/org/example/Pizzeria.java @@ -0,0 +1,108 @@ +package org.example; + +import java.util.ArrayList; +import java.util.List; +import org.example.config.PizzeriaConfig; +import org.example.order.Order; +import org.example.order.OrderQueue; +import org.example.order.OrderState; +import org.example.storage.Storage; +import org.example.worker.Baker; +import org.example.worker.Courier; + +/** + * Пиццерия. + */ +public class Pizzeria { + private final OrderQueue orderQueue; + private final Storage storage; + private final List bakerThreads; + private final List courierThreads; + private final PizzeriaConfig config; + + /** + * Конструктор пиццерии. + * + * @param config конфигурация пиццерии + */ + public Pizzeria(PizzeriaConfig config) { + this.config = config; + this.orderQueue = new OrderQueue(); + this.storage = new Storage(config.storageCapacity); + this.bakerThreads = new ArrayList<>(); + this.courierThreads = new ArrayList<>(); + } + + /** + * Запускает пиццерии. + */ + public void start() { + // запуск пекарей + for (PizzeriaConfig.BakerConfig bc : config.bakers) { + Baker baker = new Baker(bc.name, bc.cookingTimeMs, orderQueue, storage); + Thread thread = new Thread(baker, "Baker-" + bc.name); + bakerThreads.add(thread); + thread.start(); + } + + // запуск курьеров + for (PizzeriaConfig.CourierConfig cc : config.couriers) { + Courier courier = new Courier(cc.name, cc.trunkCapacity, storage); + Thread thread = new Thread(courier, "Courier-" + cc.name); + courierThreads.add(thread); + thread.start(); + } + + // заказы в очередь + for (PizzeriaConfig.OrderConfig oc : config.orders) { + Order order = new Order(oc.id); + order.setState(OrderState.QUEUED); + orderQueue.put(order); + } + + try { + Thread.sleep(config.workingTimeMs); + } catch (InterruptedException e) { + System.err.println("Pizzeria рабочее время прервано досрочно"); + Thread.currentThread().interrupt(); + } + + System.out.println("=== Пиццерия закрывается. Приём заказов остановлен. ==="); + shutdown(); + } + + /** + * Завершает работу пиццерии. + */ + private void shutdown() { + // закрываем очередь + orderQueue.shutdown(); + + // ждём пекарей + for (Thread t : bakerThreads) { + try { + t.join(); + } catch (InterruptedException e) { + System.err.println("Pizzeria прерывание при ожидании пекаря " + + t.getName()); + Thread.currentThread().interrupt(); + } + } + System.out.println("Все пекари завершили работу."); + + // закрываем склад + storage.shutdown(); + + // ждём курьеров + for (Thread t : courierThreads) { + try { + t.join(); + } catch (InterruptedException e) { + System.err.println("Pizzeri прерывание при ожидании курьера " + + t.getName()); + Thread.currentThread().interrupt(); + } + } + System.out.println("Все курьеры завершили работу. Пиццерия закрыта."); + } +} diff --git a/Task_2_2_1/app/src/main/java/org/example/config/PizzeriaConfig.java b/Task_2_2_1/app/src/main/java/org/example/config/PizzeriaConfig.java new file mode 100644 index 0000000..9769420 --- /dev/null +++ b/Task_2_2_1/app/src/main/java/org/example/config/PizzeriaConfig.java @@ -0,0 +1,58 @@ +package org.example.config; + +import com.google.gson.Gson; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import org.example.exception.ConfigLoadException; + +/** + * Конфигурация пиццерии из JSON. + */ +public class PizzeriaConfig { + + /** + * Конфиг пекаря. + */ + public static class BakerConfig { + public String name; + public int cookingTimeMs; + } + + /** + * Конфиг курьера. + */ + public static class CourierConfig { + public String name; + public int trunkCapacity; + } + + /** + * Конфиг заказа. + */ + public static class OrderConfig { + public int id; + } + + public BakerConfig[] bakers; + public CourierConfig[] couriers; + public int storageCapacity; + public int workingTimeMs; + public OrderConfig[] orders; + + /** + * Загрузка конфига. + * + * @param resourcePath путь к файлу + * @return конфигурация + */ + public static PizzeriaConfig load(String resourcePath) { + InputStream stream = PizzeriaConfig.class.getResourceAsStream(resourcePath); + if (stream == null) { + throw new ConfigLoadException("Файл не найден: " + resourcePath); + } + Reader reader = new InputStreamReader(stream); + Gson gson = new Gson(); + return gson.fromJson(reader, PizzeriaConfig.class); + } +} diff --git a/Task_2_2_1/app/src/main/java/org/example/exception/ConfigLoadException.java b/Task_2_2_1/app/src/main/java/org/example/exception/ConfigLoadException.java new file mode 100644 index 0000000..78a0d2b --- /dev/null +++ b/Task_2_2_1/app/src/main/java/org/example/exception/ConfigLoadException.java @@ -0,0 +1,15 @@ +package org.example.exception; + +/** + * Ошибка загрузки конфигурации. + */ +public class ConfigLoadException extends RuntimeException { + /** + * Конструктор. + * + * @param message сообщение + */ + public ConfigLoadException(String message) { + super(message); + } +} diff --git a/Task_2_2_1/app/src/main/java/org/example/exception/PizzeriaInterruptException.java b/Task_2_2_1/app/src/main/java/org/example/exception/PizzeriaInterruptException.java new file mode 100644 index 0000000..f206c2e --- /dev/null +++ b/Task_2_2_1/app/src/main/java/org/example/exception/PizzeriaInterruptException.java @@ -0,0 +1,16 @@ +package org.example.exception; + +/** + * Ошибка прерывания работы пиццерии. + */ +public class PizzeriaInterruptException extends RuntimeException { + /** + * Конструктор. + * + * @param message сообщение + * @param cause причина + */ + public PizzeriaInterruptException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/Task_2_2_1/app/src/main/java/org/example/order/Order.java b/Task_2_2_1/app/src/main/java/org/example/order/Order.java new file mode 100644 index 0000000..b92fb8b --- /dev/null +++ b/Task_2_2_1/app/src/main/java/org/example/order/Order.java @@ -0,0 +1,46 @@ +package org.example.order; + +/** + * Заказ на пиццу. + */ +public class Order { + private final int id; + private OrderState state; + + /** + * Конструктор заказ. + * + * @param id номер заказа + */ + public Order(int id) { + this.id = id; + } + + /** + * Геттер номера. + * + * @return номер заказа + */ + public int getId() { + return id; + } + + /** + * Состояние заказа. + * + * @return текущее состояние + */ + public OrderState getState() { + return state; + } + + /** + * Сеттер состояния. + * + * @param state новое состояние + */ + public void setState(OrderState state) { + this.state = state; + System.out.println("[" + id + "] " + state); + } +} diff --git a/Task_2_2_1/app/src/main/java/org/example/order/OrderQueue.java b/Task_2_2_1/app/src/main/java/org/example/order/OrderQueue.java new file mode 100644 index 0000000..0cc6954 --- /dev/null +++ b/Task_2_2_1/app/src/main/java/org/example/order/OrderQueue.java @@ -0,0 +1,133 @@ +package org.example.order; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import org.example.exception.PizzeriaInterruptException; + +/** + * Очередь заказов. + */ +public class OrderQueue { + private final LinkedList queue = new LinkedList<>(); + private final int capacity; + private boolean closed = false; + + /** + * Безлимитная очередь. + */ + public OrderQueue() { + this(-1); + } + + /** + * Очередь с ограниченной вместимостью. + * + * @param capacity вместимость (-1 безлимитная) + */ + public OrderQueue(int capacity) { + this.capacity = capacity; + } + + /** + * Добавляет заказ в очередь. + * + * @param order заказ + * @return true если заказ добавлен, false если очередь закрыта + */ + public synchronized boolean put(Order order) { + while (capacity > 0 && queue.size() >= capacity && !closed) { + try { + wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new PizzeriaInterruptException( + "OrderQueue: прерывание при добавлении заказа [" + + order.getId() + "]", e); + } + } + if (closed) { + return false; + } + queue.addLast(order); + notifyAll(); + return true; + } + + /** + * Забирает заказ из очереди. + * + * @return заказ или null если очередь закрыта и пуста + */ + public synchronized Order take() { + while (queue.isEmpty() && !closed) { + try { + wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new PizzeriaInterruptException( + "OrderQueue: прерывание при ожидании заказа", e); + } + } + if (queue.isEmpty()) { + return null; + } + Order order = queue.removeFirst(); + notifyAll(); + return order; + } + + /** + * Забирает несколько заказов. + * + * @param maxCount максимум заказов + * @return список заказов + */ + public synchronized List takeUpTo(int maxCount) { + while (queue.isEmpty() && !closed) { + try { + wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new PizzeriaInterruptException( + "OrderQueue: прерывание при ожидании заказов", e); + } + } + if (queue.isEmpty()) { + return new ArrayList<>(); + } + int count = Math.min(maxCount, queue.size()); + List taken = new ArrayList<>(); + for (int i = 0; i < count; i++) { + taken.add(queue.removeFirst()); + } + notifyAll(); + return taken; + } + + /** + * Закрывает очередь. + */ + public synchronized void shutdown() { + closed = true; + notifyAll(); + } + + /** + * Проверяет закрыта ли очередь. + * + * @return true если закрыта + */ + public synchronized boolean isClosed() { + return closed; + } + + /** + * Возвращает размер очереди. + * + * @return количество заказов + */ + public synchronized int size() { + return queue.size(); + } +} diff --git a/Task_2_2_1/app/src/main/java/org/example/order/OrderState.java b/Task_2_2_1/app/src/main/java/org/example/order/OrderState.java new file mode 100644 index 0000000..867c93a --- /dev/null +++ b/Task_2_2_1/app/src/main/java/org/example/order/OrderState.java @@ -0,0 +1,12 @@ +package org.example.order; + +/** + * Состояния заказа. + */ +public enum OrderState { + QUEUED, + COOKING, + STORED, + DELIVERING, + DELIVERED +} diff --git a/Task_2_2_1/app/src/main/java/org/example/storage/Storage.java b/Task_2_2_1/app/src/main/java/org/example/storage/Storage.java new file mode 100644 index 0000000..f8d0009 --- /dev/null +++ b/Task_2_2_1/app/src/main/java/org/example/storage/Storage.java @@ -0,0 +1,68 @@ +package org.example.storage; + +import java.util.List; +import org.example.order.Order; +import org.example.order.OrderQueue; +import org.example.order.OrderState; + +/** + * Склад готовых пицц. + */ +public class Storage { + private final OrderQueue queue; + + /** + * Конструктор склада. + * + * @param capacity вместимость склада + */ + public Storage(int capacity) { + this.queue = new OrderQueue(capacity); + } + + /** + * Кладёт пиццу на склад. + * + * @param order готовый заказ + */ + public void put(Order order) { + if (queue.put(order)) { + order.setState(OrderState.STORED); + } + } + + /** + * Забирает пиццы. + * + * @param maxCount максимум пицц + * @return список заказов + */ + public List takeUpTo(int maxCount) { + return queue.takeUpTo(maxCount); + } + + /** + * Закрыть склад. + */ + public void shutdown() { + queue.shutdown(); + } + + /** + * Колво пицц. + * + * @return размер склада + */ + public int size() { + return queue.size(); + } + + /** + * Закрыт ли склад. + * + * @return true если закрыт + */ + public boolean isClosed() { + return queue.isClosed(); + } +} diff --git a/Task_2_2_1/app/src/main/java/org/example/worker/Baker.java b/Task_2_2_1/app/src/main/java/org/example/worker/Baker.java new file mode 100644 index 0000000..754bba9 --- /dev/null +++ b/Task_2_2_1/app/src/main/java/org/example/worker/Baker.java @@ -0,0 +1,71 @@ +package org.example.worker; + +import org.example.exception.PizzeriaInterruptException; +import org.example.order.Order; +import org.example.order.OrderQueue; +import org.example.order.OrderState; +import org.example.storage.Storage; + +/** + * Пекарь. + */ +public class Baker implements Runnable { + private final String name; + private final int cookingTimeMs; + private final OrderQueue orderQueue; + private final Storage storage; + + /** + * Конструктор. + * + * @param name имя + * @param cookingTimeMs время + * @param orderQueue очередь + * @param storage склад + */ + public Baker(String name, int cookingTimeMs, OrderQueue orderQueue, Storage storage) { + this.name = name; + this.cookingTimeMs = cookingTimeMs; + this.orderQueue = orderQueue; + this.storage = storage; + } + + /** + * Цикл. + */ + @Override + public void run() { + while (true) { + Order order = orderQueue.take(); + if (order == null) { + break; + } + + order.setState(OrderState.COOKING); + System.out.println(" Пекарь " + name + " готовит заказ [" + order.getId() + "]"); + + try { + Thread.sleep(cookingTimeMs); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new PizzeriaInterruptException( + "Пекарь " + name + ": прерван при готовке заказа [" + + order.getId() + "]", + e); + } + + storage.put(order); + System.out.println(" Пекарь " + name + " положил заказ [" + order.getId() + + "] на склад"); + } + } + + /** + * Геттер имени. + * + * @return имя + */ + public String getName() { + return name; + } +} diff --git a/Task_2_2_1/app/src/main/java/org/example/worker/Courier.java b/Task_2_2_1/app/src/main/java/org/example/worker/Courier.java new file mode 100644 index 0000000..b236569 --- /dev/null +++ b/Task_2_2_1/app/src/main/java/org/example/worker/Courier.java @@ -0,0 +1,73 @@ +package org.example.worker; + +import java.util.List; +import org.example.exception.PizzeriaInterruptException; +import org.example.order.Order; +import org.example.order.OrderState; +import org.example.storage.Storage; + +/** + * Курьер. + */ +public class Courier implements Runnable { + private final String name; + private final int trunkCapacity; + private final Storage storage; + + private static final int DELIVERY_TIME_MS = 1500; + + /** + * Конструктор курьера. + * + * @param name имя + * @param trunkCapacity багажник + * @param storage склад + */ + public Courier(String name, int trunkCapacity, Storage storage) { + this.name = name; + this.trunkCapacity = trunkCapacity; + this.storage = storage; + } + + /** + * Цикл . + */ + @Override + public void run() { + while (true) { + List pizzas = storage.takeUpTo(trunkCapacity); + if (pizzas.isEmpty()) { + break; + } + + for (Order order : pizzas) { + order.setState(OrderState.DELIVERING); + } + + System.out.println(" Курьер " + name + " доставляет " + pizzas.size() + " пицц(у)"); + + try { + Thread.sleep(DELIVERY_TIME_MS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new PizzeriaInterruptException( + "Курьер " + name + ": прерван при доставке " + + pizzas.size() + " пицц", + e); + } + + for (Order order : pizzas) { + order.setState(OrderState.DELIVERED); + } + } + } + + /** + * Геттер имени. + * + * @return имя + */ + public String getName() { + return name; + } +} diff --git a/Task_2_2_1/app/src/main/resources/config.json b/Task_2_2_1/app/src/main/resources/config.json new file mode 100644 index 0000000..a7610aa --- /dev/null +++ b/Task_2_2_1/app/src/main/resources/config.json @@ -0,0 +1,98 @@ +{ + "bakers": [ + { + "name": "Константин Ивлев", + "cookingTimeMs": 2000 + }, + { + "name": "Гордон Рамзи", + "cookingTimeMs": 4000 + }, + { + "name": "Ник Диджовани", + "cookingTimeMs": 3000 + }, + { + "name": "Друже Обломов", + "cookingTimeMs": 5000 + } + ], + "couriers": [ + { + "name": "Еда", + "trunkCapacity": 2 + }, + { + "name": "Лавка", + "trunkCapacity": 3 + }, + { + "name": "Самокат", + "trunkCapacity": 4 + } + ], + "storageCapacity": 8, + "workingTimeMs": 30000, + "orders": [ + { + "id": 1 + }, + { + "id": 2 + }, + { + "id": 3 + }, + { + "id": 4 + }, + { + "id": 5 + }, + { + "id": 6 + }, + { + "id": 7 + }, + { + "id": 8 + }, + { + "id": 9 + }, + { + "id": 10 + }, + { + "id": 11 + }, + { + "id": 12 + }, + { + "id": 13 + }, + { + "id": 14 + }, + { + "id": 15 + }, + { + "id": 16 + }, + { + "id": 17 + }, + { + "id": 18 + }, + { + "id": 19 + }, + { + "id": 20 + } + ] +} \ No newline at end of file diff --git a/Task_2_2_1/app/src/test/java/org/example/PizzeriaTest.java b/Task_2_2_1/app/src/test/java/org/example/PizzeriaTest.java new file mode 100644 index 0000000..8c9d64c --- /dev/null +++ b/Task_2_2_1/app/src/test/java/org/example/PizzeriaTest.java @@ -0,0 +1,47 @@ +package org.example; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +import org.example.config.PizzeriaConfig; +import org.junit.jupiter.api.Test; + +/** + * Тесты. + */ +class PizzeriaTest { + + @Test + void testPizzeriaIntegration() throws InterruptedException { + PizzeriaConfig config = new PizzeriaConfig(); + + PizzeriaConfig.BakerConfig baker = new PizzeriaConfig.BakerConfig(); + baker.name = "Тест-пекарь"; + baker.cookingTimeMs = 100; + config.bakers = new PizzeriaConfig.BakerConfig[] { baker }; + + PizzeriaConfig.CourierConfig courier = new PizzeriaConfig.CourierConfig(); + courier.name = "Тест-курьер"; + courier.trunkCapacity = 5; + config.couriers = new PizzeriaConfig.CourierConfig[] { courier }; + + config.storageCapacity = 3; + config.workingTimeMs = 5000; + + PizzeriaConfig.OrderConfig o1 = new PizzeriaConfig.OrderConfig(); + o1.id = 1; + PizzeriaConfig.OrderConfig o2 = new PizzeriaConfig.OrderConfig(); + o2.id = 2; + PizzeriaConfig.OrderConfig o3 = new PizzeriaConfig.OrderConfig(); + o3.id = 3; + config.orders = new PizzeriaConfig.OrderConfig[] { o1, o2, o3 }; + + Thread pizzeriaThread = new Thread(() -> { + Pizzeria pizzeria = new Pizzeria(config); + pizzeria.start(); + }); + pizzeriaThread.start(); + pizzeriaThread.join(15000); + + assertFalse(pizzeriaThread.isAlive(), "Пиццерия должна завершиться"); + } +} diff --git a/Task_2_2_1/app/src/test/java/org/example/config/PizzeriaConfigTest.java b/Task_2_2_1/app/src/test/java/org/example/config/PizzeriaConfigTest.java new file mode 100644 index 0000000..bbd4a6d --- /dev/null +++ b/Task_2_2_1/app/src/test/java/org/example/config/PizzeriaConfigTest.java @@ -0,0 +1,90 @@ +package org.example.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.example.exception.ConfigLoadException; +import org.junit.jupiter.api.Test; + +class PizzeriaConfigTest { + + @Test + void testLoadConfigNotNull() { + PizzeriaConfig config = PizzeriaConfig.load("/config.json"); + assertNotNull(config); + } + + @Test + void testBakersCount() { + PizzeriaConfig config = PizzeriaConfig.load("/config.json"); + assertEquals(4, config.bakers.length); + } + + @Test + void testFirstBakerName() { + PizzeriaConfig config = PizzeriaConfig.load("/config.json"); + assertEquals("Константин Ивлев", config.bakers[0].name); + } + + @Test + void testFirstBakerCookingTime() { + PizzeriaConfig config = PizzeriaConfig.load("/config.json"); + assertEquals(2000, config.bakers[0].cookingTimeMs); + } + + @Test + void testCouriersCount() { + PizzeriaConfig config = PizzeriaConfig.load("/config.json"); + assertEquals(3, config.couriers.length); + } + + @Test + void testFirstCourierName() { + PizzeriaConfig config = PizzeriaConfig.load("/config.json"); + assertEquals("Еда", config.couriers[0].name); + } + + @Test + void testFirstCourierTrunkCapacity() { + PizzeriaConfig config = PizzeriaConfig.load("/config.json"); + assertEquals(2, config.couriers[0].trunkCapacity); + } + + @Test + void testStorageCapacity() { + PizzeriaConfig config = PizzeriaConfig.load("/config.json"); + assertEquals(8, config.storageCapacity); + } + + @Test + void testWorkingTime() { + PizzeriaConfig config = PizzeriaConfig.load("/config.json"); + assertEquals(30000, config.workingTimeMs); + } + + @Test + void testOrdersCount() { + PizzeriaConfig config = PizzeriaConfig.load("/config.json"); + assertEquals(20, config.orders.length); + } + + @Test + void testFirstOrderId() { + PizzeriaConfig config = PizzeriaConfig.load("/config.json"); + assertEquals(1, config.orders[0].id); + } + + @Test + void testLastOrderId() { + PizzeriaConfig config = PizzeriaConfig.load("/config.json"); + assertEquals(20, config.orders[19].id); + } + + @Test + void testLoadMissingFileThrows() { + assertThrows(ConfigLoadException.class, () -> { + PizzeriaConfig.load("/not_exists.json"); + }); + } +} diff --git a/Task_2_2_1/app/src/test/java/org/example/exception/PizzeriaInterruptExceptionTest.java b/Task_2_2_1/app/src/test/java/org/example/exception/PizzeriaInterruptExceptionTest.java new file mode 100644 index 0000000..ff3d569 --- /dev/null +++ b/Task_2_2_1/app/src/test/java/org/example/exception/PizzeriaInterruptExceptionTest.java @@ -0,0 +1,22 @@ +package org.example.exception; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +import org.junit.jupiter.api.Test; + +class PizzeriaInterruptExceptionTest { + + @Test + void testMessage() { + PizzeriaInterruptException ex = new PizzeriaInterruptException("тест", null); + assertEquals("тест", ex.getMessage()); + } + + @Test + void testCause() { + InterruptedException cause = new InterruptedException("причина"); + PizzeriaInterruptException ex = new PizzeriaInterruptException("тест", cause); + assertSame(cause, ex.getCause()); + } +} diff --git a/Task_2_2_1/app/src/test/java/org/example/order/OrderQueueTest.java b/Task_2_2_1/app/src/test/java/org/example/order/OrderQueueTest.java new file mode 100644 index 0000000..4fb7013 --- /dev/null +++ b/Task_2_2_1/app/src/test/java/org/example/order/OrderQueueTest.java @@ -0,0 +1,153 @@ +package org.example.order; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.example.exception.PizzeriaInterruptException; +import org.junit.jupiter.api.Test; + +class OrderQueueTest { + + @Test + void testPutAndTakeOrder() { + OrderQueue queue = new OrderQueue(); + Order order = new Order(1); + queue.put(order); + assertSame(order, queue.take()); + } + + @Test + void testFifoOrder() { + OrderQueue queue = new OrderQueue(); + Order o1 = new Order(1); + Order o2 = new Order(2); + Order o3 = new Order(3); + queue.put(o1); + queue.put(o2); + queue.put(o3); + + assertSame(o1, queue.take()); + assertSame(o2, queue.take()); + assertSame(o3, queue.take()); + } + + @Test + void testSize() { + OrderQueue queue = new OrderQueue(); + assertEquals(0, queue.size()); + queue.put(new Order(1)); + assertEquals(1, queue.size()); + queue.put(new Order(2)); + assertEquals(2, queue.size()); + queue.take(); + assertEquals(1, queue.size()); + } + + @Test + void testShutdownReturnNullOnEmpty() { + OrderQueue queue = new OrderQueue(); + queue.shutdown(); + assertTrue(queue.isClosed()); + assertNull(queue.take()); + } + + @Test + void testShutdownAfterPut() { + OrderQueue queue = new OrderQueue(); + queue.put(new Order(1)); + queue.shutdown(); + // можно забрать оставшийся заказ даже после shutdown + assertNotNull(queue.take()); + assertNull(queue.take()); + } + + @Test + void testShutdownWakesWaitingThread() throws InterruptedException { + OrderQueue queue = new OrderQueue(); + + Thread consumer = new Thread(() -> { + Order result = queue.take(); + assertNull(result); + }); + consumer.start(); + Thread.sleep(200); + + queue.shutdown(); + consumer.join(2000); + assertFalse(consumer.isAlive()); + } + + @Test + void testBoundedQueueBlocksWhenFull() throws InterruptedException { + OrderQueue queue = new OrderQueue(1); + queue.put(new Order(1)); + + Thread putter = new Thread(() -> queue.put(new Order(2))); + putter.start(); + Thread.sleep(200); + assertTrue(putter.isAlive(), "Поток должен ждать очередь полная"); + + queue.take(); + putter.join(2000); + assertFalse(putter.isAlive()); + assertEquals(1, queue.size()); + } + + @Test + void testTakeUpToReturnsAvailable() { + OrderQueue queue = new OrderQueue(); + queue.put(new Order(1)); + queue.put(new Order(2)); + + List taken = queue.takeUpTo(10); + assertEquals(2, taken.size()); + assertEquals(0, queue.size()); + } + + @Test + void testTakeUpToRespectsLimit() { + OrderQueue queue = new OrderQueue(); + queue.put(new Order(1)); + queue.put(new Order(2)); + queue.put(new Order(3)); + + List taken = queue.takeUpTo(2); + assertEquals(2, taken.size()); + assertEquals(1, queue.size()); + } + + @Test + void testTakeUpToOnEmptyShutdownReturnsEmpty() { + OrderQueue queue = new OrderQueue(); + queue.shutdown(); + + List taken = queue.takeUpTo(5); + assertTrue(taken.isEmpty()); + } + + @Test + void testPutReturnsFalseWhenClosed() { + OrderQueue queue = new OrderQueue(); + queue.shutdown(); + assertFalse(queue.put(new Order(1))); + assertEquals(0, queue.size()); + } + + @Test + void testTakeUpToThrowsExceptionOnInterrupt() { + OrderQueue queue = new OrderQueue(); + + Thread.currentThread().interrupt(); + + try { + queue.takeUpTo(1); + } catch (PizzeriaInterruptException e) { + assertEquals("OrderQueue: прерывание при ожидании заказов", e.getMessage()); + } + } +} diff --git a/Task_2_2_1/app/src/test/java/org/example/order/OrderTest.java b/Task_2_2_1/app/src/test/java/org/example/order/OrderTest.java new file mode 100644 index 0000000..c53cda3 --- /dev/null +++ b/Task_2_2_1/app/src/test/java/org/example/order/OrderTest.java @@ -0,0 +1,39 @@ +package org.example.order; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.Test; + +class OrderTest { + + @Test + void testNewOrderHasNoState() { + Order order = new Order(1); + assertNull(order.getState()); + } + + @Test + void testGetId() { + Order order = new Order(42); + assertEquals(42, order.getId()); + } + + @Test + void testSetStateUpdatesState() { + Order order = new Order(1); + order.setState(OrderState.QUEUED); + assertEquals(OrderState.QUEUED, order.getState()); + } + + @Test + void testStateTransitions() { + Order order = new Order(1); + order.setState(OrderState.QUEUED); + order.setState(OrderState.COOKING); + order.setState(OrderState.STORED); + order.setState(OrderState.DELIVERING); + order.setState(OrderState.DELIVERED); + assertEquals(OrderState.DELIVERED, order.getState()); + } +} diff --git a/Task_2_2_1/app/src/test/java/org/example/storage/StorageTest.java b/Task_2_2_1/app/src/test/java/org/example/storage/StorageTest.java new file mode 100644 index 0000000..ebe081d --- /dev/null +++ b/Task_2_2_1/app/src/test/java/org/example/storage/StorageTest.java @@ -0,0 +1,83 @@ +package org.example.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.example.order.Order; +import org.junit.jupiter.api.Test; + +class StorageTest { + + @Test + void testPutAndSize() { + Storage storage = new Storage(5); + assertEquals(0, storage.size()); + storage.put(new Order(1)); + assertEquals(1, storage.size()); + } + + @Test + void testTakeUpToReturnsAvailable() { + Storage storage = new Storage(5); + storage.put(new Order(1)); + storage.put(new Order(2)); + + List taken = storage.takeUpTo(10); + assertEquals(2, taken.size()); + assertEquals(0, storage.size()); + } + + @Test + void testTakeUpToRespectsLimit() { + Storage storage = new Storage(5); + storage.put(new Order(1)); + storage.put(new Order(2)); + storage.put(new Order(3)); + + List taken = storage.takeUpTo(2); + assertEquals(2, taken.size()); + assertEquals(1, storage.size()); + } + + @Test + void testCapacityBlocksWhenFull() throws InterruptedException { + Storage storage = new Storage(1); + storage.put(new Order(1)); + + Thread putter = new Thread(() -> storage.put(new Order(2))); + putter.start(); + Thread.sleep(200); + assertTrue(putter.isAlive(), "Поток должен ждать склад полный"); + + storage.takeUpTo(1); + putter.join(2000); + assertFalse(putter.isAlive()); + assertEquals(1, storage.size()); + } + + @Test + void testShutdownWakesWaitingConsumer() throws InterruptedException { + Storage storage = new Storage(5); + + Thread consumer = new Thread(() -> { + List result = storage.takeUpTo(3); + assertTrue(result.isEmpty()); + }); + consumer.start(); + Thread.sleep(200); + + storage.shutdown(); + consumer.join(2000); + assertFalse(consumer.isAlive()); + } + + @Test + void testIsClosedAfterShutdown() { + Storage storage = new Storage(3); + assertFalse(storage.isClosed()); + storage.shutdown(); + assertTrue(storage.isClosed()); + } +} diff --git a/Task_2_2_1/app/src/test/java/org/example/worker/BakerTest.java b/Task_2_2_1/app/src/test/java/org/example/worker/BakerTest.java new file mode 100644 index 0000000..fc09ff5 --- /dev/null +++ b/Task_2_2_1/app/src/test/java/org/example/worker/BakerTest.java @@ -0,0 +1,98 @@ +package org.example.worker; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import org.example.exception.PizzeriaInterruptException; +import org.example.order.Order; +import org.example.order.OrderQueue; +import org.example.order.OrderState; +import org.example.storage.Storage; +import org.junit.jupiter.api.Test; + +class BakerTest { + + @Test + void testBakerCooksAndPutsToStorage() throws InterruptedException { + OrderQueue queue = new OrderQueue(); + Order order = new Order(1); + order.setState(OrderState.QUEUED); + queue.put(order); + queue.shutdown(); // Пекарь обработает 1 заказ и завершится + + Storage storage = new Storage(5); + Baker baker = new Baker("Тест", 100, queue, storage); + Thread thread = new Thread(baker); + thread.start(); + thread.join(3000); + + assertFalse(thread.isAlive()); + assertEquals(1, storage.size()); + assertEquals(OrderState.STORED, order.getState()); + } + + @Test + void testBakerStopsOnEmptyShutdownQueue() throws InterruptedException { + OrderQueue queue = new OrderQueue(); + Storage storage = new Storage(5); + queue.shutdown(); + + Baker baker = new Baker("Тест", 100, queue, storage); + Thread thread = new Thread(baker); + thread.start(); + thread.join(2000); + + assertFalse(thread.isAlive()); + assertEquals(0, storage.size()); + } + + @Test + void testBakerProcessesMultipleOrders() throws InterruptedException { + OrderQueue queue = new OrderQueue(); + Storage storage = new Storage(5); + + Order o1 = new Order(1); + Order o2 = new Order(2); + queue.put(o1); + queue.put(o2); + queue.shutdown(); + + Baker baker = new Baker("Тест", 50, queue, storage); + Thread thread = new Thread(baker); + thread.start(); + thread.join(3000); + + assertFalse(thread.isAlive()); + assertEquals(2, storage.size()); + assertEquals(OrderState.STORED, o1.getState()); + assertEquals(OrderState.STORED, o2.getState()); + } + + @Test + void testGetName() { + Baker baker = new Baker("Иван", 100, new OrderQueue(), new Storage(1)); + assertEquals("Иван", baker.getName()); + } + + @Test + void testBakerThrowsExceptionOnInterrupt() { + OrderQueue queue = new OrderQueue(); + Order order = new Order(1); + order.setState(OrderState.QUEUED); + queue.put(order); + + Storage storage = new Storage(5); + Baker baker = new Baker("Иван", 10000, queue, storage); + + Thread.currentThread().interrupt(); + + try { + baker.run(); + } catch (PizzeriaInterruptException e) { + assertEquals("Пекарь Иван: прерван при готовке заказа [1]", e.getMessage()); + } + + assertEquals(OrderState.COOKING, order.getState()); + assertEquals(0, storage.size()); + } +} diff --git a/Task_2_2_1/app/src/test/java/org/example/worker/CourierTest.java b/Task_2_2_1/app/src/test/java/org/example/worker/CourierTest.java new file mode 100644 index 0000000..bad569d --- /dev/null +++ b/Task_2_2_1/app/src/test/java/org/example/worker/CourierTest.java @@ -0,0 +1,92 @@ +package org.example.worker; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import org.example.exception.PizzeriaInterruptException; +import org.example.order.Order; +import org.example.order.OrderState; +import org.example.storage.Storage; +import org.junit.jupiter.api.Test; + +class CourierTest { + + @Test + void testCourierDeliversFromStorage() throws InterruptedException { + Storage storage = new Storage(5); + Order order = new Order(1); + order.setState(OrderState.QUEUED); + order.setState(OrderState.COOKING); + storage.put(order); // Помещаем как готовую пиццу + storage.shutdown(); // Курьер заберёт 1 и завершится + + Courier courier = new Courier("Тест", 3, storage); + Thread thread = new Thread(courier); + thread.start(); + thread.join(5000); + + assertFalse(thread.isAlive()); + assertEquals(0, storage.size()); + assertEquals(OrderState.DELIVERED, order.getState()); + } + + @Test + void testCourierStopsOnEmptyShutdownStorage() throws InterruptedException { + Storage storage = new Storage(5); + storage.shutdown(); + + Courier courier = new Courier("Тест", 2, storage); + Thread thread = new Thread(courier); + thread.start(); + thread.join(3000); + + assertFalse(thread.isAlive()); + } + + @Test + void testCourierTakesUpToTrunkCapacity() throws InterruptedException { + Storage storage = new Storage(10); + Order o1 = new Order(1); + Order o2 = new Order(2); + Order o3 = new Order(3); + storage.put(o1); + storage.put(o2); + storage.put(o3); + storage.shutdown(); + + // Багажник на 2 первый рейс возьмёт 2, второй 1 + Courier courier = new Courier("Тест", 2, storage); + Thread thread = new Thread(courier); + thread.start(); + thread.join(5000); + + assertFalse(thread.isAlive()); + assertEquals(0, storage.size()); + assertEquals(OrderState.DELIVERED, o1.getState()); + assertEquals(OrderState.DELIVERED, o2.getState()); + assertEquals(OrderState.DELIVERED, o3.getState()); + } + + @Test + void testGetName() { + Courier courier = new Courier("Алексей", 3, new Storage(1)); + assertEquals("Алексей", courier.getName()); + } + + @Test + void testCourierThrowsExceptionOnInterrupt() { + Storage storage = new Storage(5); + Order order = new Order(1); + order.setState(OrderState.COOKING); + storage.put(order); // Одна пицца на складе + Courier courier = new Courier("Алексей", 3, storage); + + Thread.currentThread().interrupt(); + + try { + courier.run(); + } catch (PizzeriaInterruptException e) { + assertEquals("Курьер Алексей: прерван при доставке 1 пицц", e.getMessage()); + } + } +} diff --git a/Task_2_2_1/gradle.properties b/Task_2_2_1/gradle.properties new file mode 100644 index 0000000..377538c --- /dev/null +++ b/Task_2_2_1/gradle.properties @@ -0,0 +1,5 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties + +org.gradle.configuration-cache=true + diff --git a/Task_2_2_1/gradle/libs.versions.toml b/Task_2_2_1/gradle/libs.versions.toml new file mode 100644 index 0000000..8da4f83 --- /dev/null +++ b/Task_2_2_1/gradle/libs.versions.toml @@ -0,0 +1,10 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format + +[versions] +guava = "33.4.6-jre" +junit-jupiter = "5.12.1" + +[libraries] +guava = { module = "com.google.guava:guava", version.ref = "guava" } +junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } diff --git a/Task_2_2_1/gradle/wrapper/gradle-wrapper.jar b/Task_2_2_1/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..8bdaf60 Binary files /dev/null and b/Task_2_2_1/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Task_2_2_1/gradle/wrapper/gradle-wrapper.properties b/Task_2_2_1/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..2a84e18 --- /dev/null +++ b/Task_2_2_1/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Task_2_2_1/gradlew b/Task_2_2_1/gradlew new file mode 100644 index 0000000..ef07e01 --- /dev/null +++ b/Task_2_2_1/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Task_2_2_1/settings.gradle b/Task_2_2_1/settings.gradle new file mode 100644 index 0000000..633b049 --- /dev/null +++ b/Task_2_2_1/settings.gradle @@ -0,0 +1,14 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * For more detailed information on multi-project builds, please refer to https://docs.gradle.org/9.0.0/userguide/multi_project_builds.html in the Gradle documentation. + */ + +plugins { + // Apply the foojay-resolver plugin to allow automatic download of JDKs + id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' +} + +rootProject.name = 'Task_2_2_1' +include('app') diff --git a/Task_2_2_1/uml_diagram.png b/Task_2_2_1/uml_diagram.png new file mode 100644 index 0000000..09028ef Binary files /dev/null and b/Task_2_2_1/uml_diagram.png differ