A web application for managing personal music playlists, developed as a university project. The application allows authenticated users to upload songs, organize them into playlists, and play them back via an audio player.
| Branch | Description |
|---|---|
main |
Full RIA (Rich Internet Application) implementation using JavaScript modules and XMLHttpRequest for async communication |
Andrea-dev |
HTML-only implementation using Thymeleaf server-side rendering and standard form submissions |
- User registration and login with session-based authentication (cookie + session dual mechanism)
- Upload songs with metadata: title, artist, album, cover image, publication year, genre, and audio file
- Create playlists and add songs to them
- Browse playlists ordered by creation date (descending)
- View songs in a playlist in a paginated table (5 songs per row), with NEXT/PREV navigation
- Add songs to existing playlists (only songs not already in the playlist)
- Reorder songs within a playlist (RIA only)
- Song detail page with an embedded audio player
- Backend: Java (Jakarta Servlets)
- Database: SQLite (managed via DBeaver)
- Templating (HTML branch): Thymeleaf
- Frontend (RIA/main branch): Vanilla JavaScript (ES2015 modules)
- File storage: Filesystem (media files stored by path, not as BLOBs)
The server follows a layered architecture:
Controller (Servlet)
└── Service
└── DAO (Database / FileSystem)
- Controllers extend
HttpServlet(orDataBaseServletwhen DB access is needed) and handle parameter validation and routing. - Services contain business logic and are instantiated per-request for thread safety.
- DAOs handle data persistence — either via SQLite or the
FileSystemDaofor media files.
- TokenService — Manages user identity via HTTP session and an encrypted HttpOnly cookie (TTL: 1 year), enabling extended sessions across browser restarts.
- TokenBucketService — Manages a pool of database connections using the Token Bucket pattern, avoiding the overhead of creating a new connection per request while respecting SQLite's thread-safety constraints.
- TemplateService — Singleton wrapper around the Thymeleaf engine; accepts a template name, request/response, and a data map to render HTML pages.
- Reader / Sender (RIA only) — Utility classes for deserializing JSON request bodies and serializing Java objects to JSON responses.
- FileSystemDao — Stores uploaded media files on disk under a
<user-id>/<file-name>path structure, keeping the database free of BLOBs and enabling more efficient filesystem queries per user.
Built with SQLite. Media file paths (audio and cover image) are stored as TEXT rather than BLOBs for performance and portability.
-- Users
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
surname TEXT NOT NULL,
username TEXT NOT NULL,
password TEXT NOT NULL
);
-- Predefined music genres
CREATE TABLE genre_type (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL
);
-- Songs (owned by a user)
CREATE TABLE song (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
audio_path TEXT NOT NULL,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
album_name TEXT NOT NULL,
artist TEXT NOT NULL,
publication DATE NOT NULL,
cover TEXT DEFAULT 'default.png',
genre INTEGER NOT NULL REFERENCES genre_type(id)
);
-- Playlists (created by a user)
CREATE TABLE playlist (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
custom_order INTEGER DEFAULT 0,
date DATE NOT NULL
);
-- Many-to-many: songs in playlists, with display order
CREATE TABLE playlist_song (
playlist_id INTEGER NOT NULL REFERENCES playlist(id) ON DELETE CASCADE,
song_id INTEGER NOT NULL REFERENCES song(id) ON DELETE CASCADE,
ui_order INTEGER NOT NULL,
PRIMARY KEY (playlist_id, song_id)
);| Page | Description |
|---|---|
| Login / Register | Single page toggled via Thymeleaf th:if (HTML) or rendered dynamically (RIA) |
| Home | Lists all user playlists ordered by creation date; navbar links to song/playlist creation |
| Create Song | Form to upload a new song with all metadata and media files |
| Create Playlist | Form to name a playlist and select songs to include |
| Playlist | Paginated song table (5 per row); form to add more songs; links to song detail |
| Song (Player) | Displays full song metadata and an HTML5 audio player |
The RIA implementation uses ES2015 modules split into two categories:
Exposes get(url, onLoad, onError) and post(url, body, onLoad, onError) — thin wrappers around XMLHttpRequest for communicating with the Java servlets asynchronously.
Rather than embedding hidden HTML in the page, UI is built programmatically via a custom element(tag, props) function. It wraps document.createElement with an internal clone cache for efficiency, and accepts a children array for nested composition. Pages are generated by IIFE-wrapped functions that keep static structure in their closure, re-populating only dynamic content on each render.
This approach was chosen over the in-page hidden HTML pattern to reduce initial page weight and to prevent users from manually revealing or submitting hidden form fields.
- No BLOB storage — Media files are saved to the filesystem under
uploads/<user-id>/; the DB stores only paths. - SQLite — Chosen for zero-configuration setup, portability, and ease of testing with DBeaver.
- Playlist ordering —
ui_order(auto-increment) inplaylist_songtracks insertion order; the RIA branch additionally supports manual reordering via a dedicatedReorderservlet. - Registration — Added beyond the base spec; implemented as a variant of the login page using
th:ifin Thymeleaf (HTML branch) / conditional rendering (RIA branch). - Extended navigation — Song creation and playlist creation were moved to dedicated pages accessible via a navbar, improving UX without adding new actions to the spec.
- Thread-safe connections —
TokenBucketServicepools and recycles SQLite connections with TTL-based cleanup on a background thread.