diff --git a/README.md b/README.md
index 10f5017..125b6d5 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,77 @@
-# Java URL Shortener
+# Shorten
-See full README content in project documentation.
\ No newline at end of file
+A lightweight URL shortener built using plain Java with `com.sun.net.httpserver.HttpServer`, H2 Database, and vanilla HTML/CSS/JS frontend.
+
+## โ
Features
+
+* ๐ **Anonymous URL shortening**: Convert long URLs into short ones instantly.
+* ๐งโ๐ป **User login**: Create an account and log in.
+* ๐ **Custom short URLs**: Logged-in users can create their own alias URLs.
+* โช๏ธ **Redirection**: Short URLs automatically redirect to the long ones.
+
+## ๐ ๏ธ Tech Stack
+
+| Layer | Technology |
+| -------- | ----------------------------------------- |
+| Frontend | HTML, CSS, JavaScript (no framework) |
+| Backend | Java, `com.sun.net.httpserver.HttpServer` |
+| Database | H2 using JDBC |
+| Logging | SLF4J |
+| Testing | JUnit 5, Mockito |
+| CI/CD | GitHub Actions |
+
+```
+
+## ๐ Getting Started
+
+### Prerequisites
+
+* Java 17+
+* Maven
+
+### Run Application
+
+```bash
+mvn clean package
+java -jar target/shorten.jar
+```
+
+Visit `http://localhost:8080` in your browser.
+
+### Database
+
+* H2 in-memory mode used.
+* JDBC with prepared statements.
+
+### GitHub Actions
+
+* CI configured in `.github/workflows/ci.yml` to run tests on every PR and merge to `main`.
+
+## ๐ API Endpoints
+
+| Endpoint | Method | Description |
+| --------------- | ------ | ------------------------------ |
+| `/shorten` | POST | Shortens a given long URL |
+| `/s/{shortUrl}` | GET | Redirects to original long URL |
+| `/register` | POST | Register a new user |
+| `/login` | POST | Log in existing user |
+
+## ๐งช Testing
+
+* All core components are tested with JUnit 5
+* Mocked database and auth dependencies with Mockito
+
+## ๐ Project Management
+
+* [ ] GitHub **Issues** created for each task
+* [ ] Separate **branches** for every feature
+* [ ] Pull Requests with self-review
+* [ ] CI runs on every PR and merge
+
+## ๐ License
+
+This project is licensed under the MIT License.
+
+---
+
+Built with โค๏ธ using pure Java.
diff --git a/pom.xml b/pom.xml
index b88700e..7c571a1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -49,6 +49,12 @@
5.12.0
test
+
+ org.projectlombok
+ lombok
+ 1.18.38
+ provided
+
diff --git a/src/main/java/urlshortener/Server.java b/src/main/java/urlshortener/Server.java
index f405286..59cca9c 100644
--- a/src/main/java/urlshortener/Server.java
+++ b/src/main/java/urlshortener/Server.java
@@ -5,9 +5,11 @@
import java.net.InetSocketAddress;
import java.sql.*;
+import lombok.extern.slf4j.Slf4j;
import urlshortener.config.AppConfig;
import urlshortener.service.*;
+@Slf4j
public class Server {
private static Connection conn;
@@ -29,7 +31,7 @@ public static void main(String[] args) throws Exception {
server.setExecutor(null);
server.start();
- System.out.println("Server running on http://localhost:8080/");
+ log.info("Server running on http://localhost:8080/");
}
public static Connection getConn() {
diff --git a/src/main/java/urlshortener/service/LoginHandler.java b/src/main/java/urlshortener/service/LoginHandler.java
index 6599d60..fd5033e 100644
--- a/src/main/java/urlshortener/service/LoginHandler.java
+++ b/src/main/java/urlshortener/service/LoginHandler.java
@@ -2,6 +2,7 @@
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
+import lombok.extern.slf4j.Slf4j;
import urlshortener.utils.HelperMethods;
import java.io.File;
@@ -15,6 +16,7 @@
import java.sql.ResultSet;
import java.sql.SQLException;
+@Slf4j
public class LoginHandler implements HttpHandler {
private static Connection conn;
@@ -52,16 +54,16 @@ public void handle(HttpExchange exchange) throws IOException {
ResultSet rs = ps.executeQuery();
if (rs.next()) {
- System.out.println("Login successful for user: " + username);
+ log.info("Login successful for user: {}", username);
String encodedUsername = URLEncoder.encode(username, StandardCharsets.UTF_8.toString());
exchange.getResponseHeaders().set("Location", "/index?username=" + encodedUsername);
exchange.sendResponseHeaders(302, -1);
} else {
- System.out.println("Invalid credentials for user: " + username);
+ log.info("Invalid credentials for user: {}", username);
HelperMethods.respond(exchange, 401, "Invalid credentials");
}
} catch (SQLException e) {
- System.err.println("Error querying user: " + e.getMessage());
+ log.error("Error querying user: {}", e.getMessage());
HelperMethods.respond(exchange, 500, "Database error");
}
}
diff --git a/src/main/java/urlshortener/service/RegisterHandler.java b/src/main/java/urlshortener/service/RegisterHandler.java
index d45ed20..654e51e 100644
--- a/src/main/java/urlshortener/service/RegisterHandler.java
+++ b/src/main/java/urlshortener/service/RegisterHandler.java
@@ -2,6 +2,7 @@
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
+import lombok.extern.slf4j.Slf4j;
import urlshortener.utils.HelperMethods;
import java.io.File;
@@ -12,6 +13,7 @@
import java.sql.PreparedStatement;
import java.sql.SQLException;
+@Slf4j
public class RegisterHandler implements HttpHandler {
private static Connection conn;
@@ -48,14 +50,15 @@ public void handle(HttpExchange exchange) throws IOException {
ps.setString(2, password);
int rowsAffected = ps.executeUpdate();
if (rowsAffected > 0) {
- System.out.println("User registered successfully: " + username);
+ log.info("User registered successfully: {}", username);
exchange.getResponseHeaders().set("Location", "/index");
exchange.sendResponseHeaders(302, -1);
} else {
+ log.error("Failed to register user {}", username);
HelperMethods.respond(exchange, 500, "Failed to register user");
}
} catch (SQLException e) {
- System.err.println("Error inserting user: " + e.getMessage());
+ log.error("Error inserting user: {}", e.getMessage());
HelperMethods.respond(exchange, 400, "User already exists");
}
}