Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 3 additions & 4 deletions files/Task.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
id,type,name,status,description,class,epicId,startTime,duration,endTime
1,TASK,Task001,NEW,Description,Task,Null,2024-02-02T11:00,60,2024-02-02T12:00
2,EPIC,Epic001,NEW,Description,Epic,2,2025-01-01T10:00,140,2025-01-01T12:30
3,SUBTASK,Subtask001,NEW,Description,Subtask,2,2025-01-01T10:00,70,2025-01-01T11:10
4,SUBTASK,Subtask002,NEW,Description,Subtask,2,2025-01-01T11:20,70,2025-01-01T12:30
1,TASK,Task001,NEW,Description,Task,Null,Null,Null,Null
2,EPIC,Epic001,NEW,Description,Epic,2,Null,Null,Null
3,SUBTASK,Subtask001,NEW,Description,Subtask,2,Null,Null,Null
9 changes: 9 additions & 0 deletions java-kanban.iml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,14 @@
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../m1-t25-api/lib/gson-2.9.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component>
</module>
68 changes: 68 additions & 0 deletions src/http/HttpTaskServer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package http;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.sun.net.httpserver.HttpServer;
import http.adapters.DurationAdapter;
import http.adapters.LocalDateTimeAdapter;
import http.handlers.*;
import managers.InMemoryTaskManager;
import managers.TaskManager;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.time.Duration;
import java.time.LocalDateTime;

public class HttpTaskServer {
private static final int PORT = 8080;

private final HttpServer kanbanServer;
private final TaskManager manager;


public HttpTaskServer(TaskManager manager) throws IOException {
this.manager = manager;

kanbanServer = HttpServer.create(new InetSocketAddress(PORT), 0);
kanbanServer.createContext("/tasks", new TaskHandler(manager));
kanbanServer.createContext("/subtasks", new SubtaskHandler(manager));
kanbanServer.createContext("/epics", new EpicHandler(manager));
kanbanServer.createContext("/history", new HistoryHandler(manager));
kanbanServer.createContext("/prioritized", new PrioritizedHandler(manager));
}

public static Gson getGson() {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter());
gsonBuilder.registerTypeAdapter(Duration.class, new DurationAdapter());
return gsonBuilder.create();
}

public void start() {
kanbanServer.start();
System.out.println("HTTP-сервер запущен на " + PORT + " порту!");
}

public void stop() {
kanbanServer.stop(3);
}

public static void main(String[] args) {
try {
TaskManager manager = new InMemoryTaskManager();
HttpTaskServer server = new HttpTaskServer(manager);
server.start();

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("\nОстанавливаем сервер...");
server.stop();
}));

System.out.println("Нажмите Ctrl+C для остановки сервера");

} catch (IOException e) {
System.err.println("Не удалось запустить сервер: " + e.getMessage());
}
}
}
28 changes: 28 additions & 0 deletions src/http/adapters/DurationAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package http.adapters;

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.time.Duration;

public class DurationAdapter extends TypeAdapter<Duration> {
@Override
public void write(JsonWriter jsonWriter, Duration duration) throws IOException {
if (duration == null) {
jsonWriter.nullValue();
} else {
jsonWriter.value(duration.toMinutes());
}
}

@Override
public Duration read(JsonReader jsonReader) throws IOException {
if (jsonReader.peek() == com.google.gson.stream.JsonToken.NULL) {
jsonReader.nextNull();
return null;
}
return Duration.ofMinutes(jsonReader.nextInt());
}
}
29 changes: 29 additions & 0 deletions src/http/adapters/LocalDateTimeAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package http.adapters;

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class LocalDateTimeAdapter extends TypeAdapter<LocalDateTime> {
@Override
public void write(JsonWriter jsonWriter, LocalDateTime localDate) throws IOException {
if (localDate == null) {
jsonWriter.nullValue();
} else {
jsonWriter.value(localDate.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
}
}

@Override
public LocalDateTime read(JsonReader jsonReader) throws IOException {
if (jsonReader.peek() == com.google.gson.stream.JsonToken.NULL) {
jsonReader.nextNull();
return null;
}
return LocalDateTime.parse(jsonReader.nextString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
}
51 changes: 51 additions & 0 deletions src/http/handlers/BaseHttpHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package http.handlers;

import com.sun.net.httpserver.HttpExchange;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;

public class BaseHttpHandler {

protected void sendText(HttpExchange h, String responseString, int responseCode) throws IOException {
byte[] resp = responseString.getBytes(StandardCharsets.UTF_8);
h.getResponseHeaders().set("Content-Type", "application/json;charset=utf-8");
h.sendResponseHeaders(responseCode, resp.length);
try (OutputStream os = h.getResponseBody()) {
os.write(resp);
}
}

protected void sendNotFound(HttpExchange h) throws IOException {
byte[] resp = "Not Found".getBytes(StandardCharsets.UTF_8);
h.getResponseHeaders().add("Content-Type", "text/plain");
h.sendResponseHeaders(404, resp.length);
h.getResponseBody().write(resp);
h.close();
}

protected void sendHasOverlaps(HttpExchange h) throws IOException {
byte[] resp = "Not Acceptable".getBytes(StandardCharsets.UTF_8);
h.getResponseHeaders().add("Content-Type", "text/plain");
h.sendResponseHeaders(406, resp.length);
h.getResponseBody().write(resp);
h.close();
}

protected Integer getId(String path) {
try {
String[] parts = path.split("/");
if (parts.length >= 3) {
return Integer.parseInt(parts[2]);
}
} catch (Exception e) {
System.out.println("Cannot get ID from path: " + path);
}
return null;
}

protected String readText(HttpExchange h) throws IOException {
return new String(h.getRequestBody().readAllBytes(), StandardCharsets.UTF_8);
}
}
96 changes: 96 additions & 0 deletions src/http/handlers/EpicHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package http.handlers;

import com.google.gson.Gson;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import exceptions.TaskValidationException;
import http.HttpTaskServer;
import managers.TaskManager;
import tasks.Epic;
import tasks.Subtask;

import java.io.IOException;
import java.util.List;

public class EpicHandler extends BaseHttpHandler implements HttpHandler {

private Gson gson;
private TaskManager taskManager;

public EpicHandler(TaskManager taskManager) {
this.taskManager = taskManager;
this.gson = HttpTaskServer.getGson();
}

@Override
public void handle(HttpExchange exchange) throws IOException {
try {
String path = exchange.getRequestURI().getPath();
Integer idFromPath = getId(path);
String method = exchange.getRequestMethod();
System.out.println("Request: " + method + " " + path + " (id: " + idFromPath + ")");
switch (method) {
case "GET": {
if (idFromPath == null) {
List<Epic> epicList = taskManager.getEpicList();
String response = gson.toJson(epicList);
System.out.println("Получили список эпиков");
sendText(exchange, response, 200);
break;
} else if ((path.split("/").length > 3) && "subtasks".equals(path.split("/")[3])) {
Epic epic = taskManager.getEpicForId(idFromPath);
if (epic != null) {
List<Subtask> epicSubList = taskManager.getEpicSubtask(idFromPath);
String response = gson.toJson(epicSubList);
System.out.println("Получили список подзадач эпика");
sendText(exchange, response, 200);
break;
} else {
sendNotFound(exchange);
}
} else {
Epic epic = taskManager.getEpicForId(idFromPath);
if (epic != null) {
String response = gson.toJson(epic);
System.out.println("Получили эпик по id " + idFromPath);
sendText(exchange, response, 200);
} else {
sendNotFound(exchange);
}
break;
}
}
case "POST": {
String body = readText(exchange);
Epic epic = gson.fromJson(body, Epic.class);
try {
taskManager.addEpic(epic);
int epicId = epic.getId();
System.out.println("Создали эпик с id: " + epicId);
String response = gson.toJson(epic);
sendText(exchange, response, 201);
} catch (TaskValidationException ex) {
sendHasOverlaps(exchange);
}
break;
}
case "DELETE": {
taskManager.removeEpicForId(idFromPath);
System.out.println("Удалили эпик по id " + idFromPath);
exchange.sendResponseHeaders(200, 0);
exchange.close();
break;
}
default: {
System.out.println("/epics получил: " + exchange.getRequestMethod());
exchange.sendResponseHeaders(405, 0);
exchange.close();
break;
}
}
} catch (Exception e) {
System.err.println("Ошибка обработки: " + e.getMessage());
e.printStackTrace();
}
}
}
44 changes: 44 additions & 0 deletions src/http/handlers/HistoryHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package http.handlers;

import com.google.gson.Gson;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import http.HttpTaskServer;
import managers.TaskManager;
import tasks.Task;

import java.io.IOException;
import java.util.List;

public class HistoryHandler extends BaseHttpHandler implements HttpHandler {

private Gson gson;
private TaskManager taskManager;

public HistoryHandler(TaskManager taskManager) {
this.taskManager = taskManager;
this.gson = HttpTaskServer.getGson();
}

@Override
public void handle(HttpExchange exchange) throws IOException {
String path = exchange.getRequestURI().getPath();
String method = exchange.getRequestMethod();
System.out.println("Request: " + method + " " + path);
switch (method) {
case "GET": {
List<Task> history = taskManager.getHistory();
String response = gson.toJson(history);
System.out.println("Получили историю");
sendText(exchange, response, 200);
return;
}
default: {
System.out.println("/history получил: " + exchange.getRequestMethod());
exchange.sendResponseHeaders(405, 0);
exchange.close();
break;
}
}
}
}
Loading